diff --git a/.vscode/settings.json b/.vscode/settings.json index 8c46f0f08b..f6c54aa57a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -25,11 +25,14 @@ ], "cSpell.words": [ "artalk", + "artplayer", + "bilibili", "bumpp", "commitlint", "chartjs", "composables", "darkmode", + "dashjs", "devtool", "docsearch", "domhandler", @@ -60,6 +63,7 @@ "meilisearch", "meteorlxy", "mhchem", + "mpegts", "mindmap", "nord", "npmmirror", @@ -90,6 +94,7 @@ "twoslash", "umami", "unmount", + "vidstack", "vuejs", "vuepress", "vueuse", diff --git a/plugins/features/plugin-media/package.json b/plugins/features/plugin-media/package.json new file mode 100644 index 0000000000..4ced26d953 --- /dev/null +++ b/plugins/features/plugin-media/package.json @@ -0,0 +1,78 @@ +{ + "name": "@vuepress/plugin-media", + "version": "2.0.0-rc.91", + "description": "VuePress plugin - media", + "keywords": [ + "vuepress-plugin", + "vuepress", + "plugin", + "medium", + "zoom", + "image" + ], + "homepage": "https://ecosystem.vuejs.press/plugins/features/media.html", + "bugs": { + "url": "https://github.com/vuepress/ecosystem/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/vuepress/ecosystem.git", + "directory": "plugins/features/plugin-media" + }, + "license": "MIT", + "author": { + "name": "Mr.Hope", + "email": "mister-hope@outlook.com", + "url": "https://mister-hope.com" + }, + "type": "module", + "exports": { + ".": "./lib/node/index.js", + "./client": "./lib/client/index.js", + "./package.json": "./package.json" + }, + "main": "./lib/node/index.js", + "types": "./lib/node/index.d.ts", + "files": [ + "lib" + ], + "scripts": { + "build": "tsc -b tsconfig.build.json", + "bundle": "rollup -c rollup.config.ts --configPlugin esbuild", + "clean": "rimraf --glob ./lib ./*.tsbuildinfo", + "copy": "cpx \"src/**/*.css\" lib" + }, + "dependencies": { + "@vuepress/helper": "workspace:*", + "@vueuse/core": "catalog:", + "vue": "catalog:" + }, + "peerDependencies": { + "artplayer": "^5.3.0", + "dashjs": "4.7.4", + "hls.js": "^1.6.13", + "mpegts.js": "^1.7.3", + "vidstack": "^1.12.13", + "vuepress": "catalog:" + }, + "peerDependenciesMeta": { + "artplayer": { + "optional": true + }, + "dashjs": { + "optional": true + }, + "hls.js": { + "optional": true + }, + "mpegts.js": { + "optional": true + }, + "vidstack": { + "optional": true + } + }, + "publishConfig": { + "access": "public" + } +} diff --git a/plugins/features/plugin-media/rollup.config.ts b/plugins/features/plugin-media/rollup.config.ts new file mode 100644 index 0000000000..86a1f5ac55 --- /dev/null +++ b/plugins/features/plugin-media/rollup.config.ts @@ -0,0 +1,9 @@ +import { rollupBundle } from '../../../scripts/rollup.js' + +export default [ + ...rollupBundle('node/index'), + ...rollupBundle({ + base: 'client', + files: ['config', 'index'], + }), +] diff --git a/plugins/features/plugin-media/src/client/components/ArtPlayer.ts b/plugins/features/plugin-media/src/client/components/ArtPlayer.ts new file mode 100644 index 0000000000..70bb7fa081 --- /dev/null +++ b/plugins/features/plugin-media/src/client/components/ArtPlayer.ts @@ -0,0 +1,302 @@ +import { LoadingIcon, keys } from '@vuepress/helper/client' +import type Artplayer from 'artplayer' +import type { Option as ArtPlayerInitOptions } from 'artplayer' +import type { PropType, VNode } from 'vue' +import { camelize, defineComponent, h, onMounted, onUnmounted, ref } from 'vue' +import { usePageLang } from 'vuepress/client' + +import type { ArtPlayerOptions } from '../../shared/index.js' +import { useSize } from '../composables/index.js' +import { getLink } from '../utils/getLink.js' +import { + SUPPORTED_VIDEO_TYPES, + getTypeByUrl, + registerMseDash, + registerMseFlv, + registerMseHls, +} from '../utils/registerMse.js' + +import '../styles/art-player.css' + +const BOOLEAN_TRUE_ATTRS = [ + 'no-fullscreen', + 'no-hotkey', + 'no-playback-rate', + 'no-setting', + 'no-mutex', + 'no-plays-inline', +] as const + +const BOOLEAN_FALSE_ATTRS = [ + 'airplay', + 'autoplay', + 'aspect-ratio', + 'auto-mini', + 'auto-size', + 'auto-orientation', + 'auto-playback', + 'fast-forward', + 'flip', + 'fullscreen-web', + 'lock', + 'loop', + 'is-live', + 'muted', + 'mini-progress-bar', + 'pip', + 'screenshot', + 'subtitle-offset', +] as const + +// Note: This should be updated with https://github.com/zhw2590582/ArtPlayer/blob/master/packages/artplayer/src/i18n/index.js +const SUPPORTED_LANG_NAME = [ + 'en', + 'pl', + 'cs', + 'es', + 'fa', + 'fr', + 'id', + 'ru', + 'tr', +] +const SUPPORTED_LANG_CODE = ['zh-cn', 'zh-tw'] + +type KebabCaseToCamelCase< + S extends string, + Cap extends boolean = false, +> = S extends `${infer Head}-${infer Tail}` + ? `${Cap extends true ? Capitalize : Head}${KebabCaseToCamelCase< + Tail, + true + >}` + : Cap extends true + ? Capitalize + : S + +type ArtPlayerBooleanOptionKey = + | (typeof BOOLEAN_FALSE_ATTRS extends readonly (infer T extends string)[] + ? KebabCaseToCamelCase + : never) + | (typeof BOOLEAN_TRUE_ATTRS extends readonly (infer T extends string)[] + ? T extends `no-${infer Key}` + ? KebabCaseToCamelCase + : never + : never) + +declare const ART_PLAYER_OPTIONS: ArtPlayerOptions + +const getLang = (lang: string): string => { + const langCode = lang.toLowerCase() + const [langName] = langCode.split('-') + + return SUPPORTED_LANG_CODE.includes(langCode) + ? langCode + : SUPPORTED_LANG_NAME.includes(langName) + ? langName + : langName === 'zh' + ? 'zh-cn' + : 'en' +} + +export const ArtPlayer = defineComponent({ + name: 'ArtPlayer', + + inheritAttrs: false, + + props: { + /** + * Video Source URL + * + * 视频源文件地址 + */ + src: { + type: String, + required: true, + }, + + /** + * Video Type + * + * 视频类型 + */ + type: String, + + /** + * Video poster + * + * 视频封面 + */ + poster: String, + + /** + * Video title + * + * 视频标题 + */ + title: String, + + /** + * Component width + * + * 组件宽度 + */ + width: { + type: [String, Number], + default: '100%', + }, + + /** + * Component height + * + * 组件高度 + */ + height: [String, Number], + + /** + * Component width / height ratio + * + * 组件长宽比 + */ + ratio: { + type: [String, Number], + default: 16 / 9, + }, + + /** + * ArtPlayer config + * + * ArtPlayer 配置 + */ + config: Object as PropType>, + + /** + * Customize Artplayer + * + * 对 Artplayer 进行自定义 + */ + customPlayer: Function as PropType< + ( + player: Artplayer, + ) => Artplayer | Promise | Promise | void + >, + }, + + setup(props, { attrs }) { + const lang = usePageLang() + const { el, width, height, resize } = useSize(props, 0) + + const loaded = ref(false) + let artPlayerInstance: Artplayer | null = null + + const getInitOptions = (): ArtPlayerInitOptions => { + const initOptions: ArtPlayerInitOptions = { + theme: '#3eaf7c', + ...ART_PLAYER_OPTIONS, + + container: el.value!, + poster: props.poster, + url: getLink(props.src), + type: props.type ?? getTypeByUrl(props.src), + lang: getLang(lang.value), + ...props.config, + // This option must be set false to avoid problems + useSSR: false, + } + + const attrsKeys = keys(attrs) + + BOOLEAN_TRUE_ATTRS.forEach((config) => { + if (attrsKeys.includes(config)) + initOptions[ + camelize(config.replace(/^no-/, '')) as ArtPlayerBooleanOptionKey + ] = false + }) + BOOLEAN_FALSE_ATTRS.forEach((config) => { + if (attrsKeys.includes(config)) + initOptions[camelize(config) as ArtPlayerBooleanOptionKey] = true + }) + + // Auto config mse + if (initOptions.type) { + // eslint-disable-next-line no-multi-assign + const customType = (initOptions.customType ??= {}) + + if (SUPPORTED_VIDEO_TYPES.includes(initOptions.type.toLowerCase())) + switch (initOptions.type.toLowerCase()) { + case 'm3u8': + case 'hls': + customType[initOptions.type] ??= ( + video: HTMLVideoElement, + src: string, + player: Artplayer, + ): Promise => + registerMseHls(video, src, (destroy) => { + player.on('destroy', destroy) + }) + break + + case 'flv': + case 'ts': + customType[initOptions.type] ??= ( + video: HTMLVideoElement, + src: string, + player: Artplayer, + ): Promise => + registerMseFlv(video, src, (destroy) => { + player.on('destroy', destroy) + }) + break + + case 'mpd': + case 'dash': + customType[initOptions.type] ??= ( + video: HTMLVideoElement, + src: string, + player: Artplayer, + ): Promise => + registerMseDash(video, src, (destroy) => { + player.on('destroy', destroy) + }) + break + + default: + } + else + // eslint-disable-next-line no-console + console.warn( + `[components]: ArtPlayer does not support current file type ${initOptions.type}!`, + ) + } + + return initOptions + } + + onMounted(async () => { + const { default: Artplayer } = await import( + /* webpackChunkName: "artplayer" */ 'artplayer' + ) + const player = new Artplayer(getInitOptions()) + + artPlayerInstance = (await props.customPlayer?.(player)) ?? player + loaded.value = true + resize() + }) + + onUnmounted(() => { + artPlayerInstance?.destroy() + }) + + return (): (VNode | null)[] => [ + h('div', { + ref: el, + class: 'vp-artplayer', + style: { + width: width.value, + height: height.value, + }, + }), + loaded.value ? null : h(LoadingIcon), + ] + }, +}) diff --git a/plugins/features/plugin-media/src/client/components/BiliBili.ts b/plugins/features/plugin-media/src/client/components/BiliBili.ts new file mode 100644 index 0000000000..993da3bbd5 --- /dev/null +++ b/plugins/features/plugin-media/src/client/components/BiliBili.ts @@ -0,0 +1,147 @@ +import { LoadingIcon } from '@vuepress/helper/client' +import type { VNode } from 'vue' +import { computed, defineComponent, h, ref } from 'vue' + +import { useSize } from '../composables/index.js' +import { videoIframeAllow } from '../utils/index.js' + +import '../styles/bili-bili.css' + +const VIDEO_LINK = 'https://player.bilibili.com/player.html' + +export const BiliBili = defineComponent({ + name: 'BiliBili', + + props: { + /** + * BiliBili video id + * + * B 站视频 ID + */ + bvid: String, + + /** + * BiliBili video aid + * + * B 站视频 a ID + */ + aid: String, + + /** + * BiliBili video cid + * + * B 站视频 CID + */ + cid: String, + + /** + * BiliBili video title + * + * B 站视频标题 + */ + title: { + type: String, + default: 'A BiliBili video', + }, + + /** + * BiliBili video page + * + * B 站视频分页 + */ + page: { + type: [String, Number], + default: 1, + }, + + /** + * Component width + * + * 组件宽度 + */ + width: { + type: [String, Number], + default: '100%', + }, + + /** + * Component height + * + * 组件高度 + */ + height: [String, Number], + + /** + * Component width / height ratio + * + * 组件长宽比 + */ + ratio: { + type: [String, Number], + default: 16 / 9, + }, + + /** + * Start time in seconds + * + * 基于秒数的开始时间 + */ + time: { + type: [String, Number], + default: 0, + }, + + /** + * Whether autoplay + * + * 是否自动播放 + */ + autoplay: Boolean, + }, + + setup(props) { + const { el, width, height, resize } = useSize(props) + + const loaded = ref(false) + + const videoLink = computed(() => { + const { aid, bvid, cid, autoplay, time, page } = props + + return aid && cid + ? `${VIDEO_LINK}?aid=${aid}&cid=${cid}&t=${time}&autoplay=${ + autoplay ? 1 : 0 + }&p=${page}` + : bvid + ? `${VIDEO_LINK}?bvid=${bvid}&t=${time}&autoplay=${autoplay ? 1 : 0}` + : null + }) + + return (): (VNode | null)[] => + videoLink.value + ? [ + h( + 'div', + { class: 'bilibili-desc' }, + h('a', { class: 'sr-only', href: videoLink.value }, props.title), + ), + h('iframe', { + ref: el, + // Tip: `https://www.bilibili.com/blackboard/newplayer.html?bvid=${props.bvid}&as_wide=1&page=1` only support whitelist sites now + src: videoLink.value, + title: props.title, + class: 'bilibili-iframe', + allow: videoIframeAllow, + style: { + width: width.value, + height: loaded.value ? height.value : 0, + }, + onLoad: () => { + loaded.value = true + resize() + }, + }), + loaded.value ? null : h(LoadingIcon), + ] + : [] + }, +}) diff --git a/plugins/features/plugin-media/src/client/components/VidStack.ts b/plugins/features/plugin-media/src/client/components/VidStack.ts new file mode 100644 index 0000000000..6269c66459 --- /dev/null +++ b/plugins/features/plugin-media/src/client/components/VidStack.ts @@ -0,0 +1,144 @@ +import type { ExactLocaleConfig } from '@vuepress/helper/client' +import { isArray, isString, useLocaleConfig } from '@vuepress/helper/client' +import type { + DASHNamespaceLoader, + DefaultLayoutProps, + HLSConstructorLoader, + PlayerSrc, + TextTrackInit, +} from 'vidstack' +import type { MediaPlayerElement } from 'vidstack/elements' +import type { VidstackPlayerConfig } from 'vidstack/global/player' +import type { PropType, VNode } from 'vue' +import { defineComponent, h, onBeforeUnmount, onMounted, shallowRef } from 'vue' + +import type { VidstackLocaleData } from '../../shared/index.js' +import { getLink } from '../utils/getLink.js' + +import 'vidstack/player/styles/default/theme.css' +import 'vidstack/player/styles/default/layouts/audio.css' +import 'vidstack/player/styles/default/layouts/video.css' +import '../styles/vidstack.css' + +declare const DASHJS_INSTALLED: boolean +declare const HLS_JS_INSTALLED: boolean +declare const VIDSTACK_LOCALES: ExactLocaleConfig + +export const VidStack = defineComponent({ + name: 'VidStack', + + props: { + /** + * sources + */ + src: { + type: [String, Array, Object] as PropType, + required: true, + }, + + /** + * tracks + */ + tracks: { + type: Array as PropType, + default: () => [], + }, + + /** + * poster + */ + poster: String, + + /** + * thumbnails + */ + thumbnails: String, + + /** + * title + */ + title: String, + + /** + * VidStack player options + */ + player: { + type: Object as PropType< + Omit< + VidstackPlayerConfig, + 'poster' | 'sources' | 'src' | 'target' | 'title' | 'tracks' + > + >, + }, + + /** + * VidStack layout options + */ + layout: { + type: Object as PropType>, + }, + + /** + * Dark mode + */ + darkmode: Boolean, + }, + + setup(props) { + const vidstack = shallowRef() + const locale = useLocaleConfig(VIDSTACK_LOCALES) + + let player: MediaPlayerElement | null = null + + onMounted(async () => { + if (__VUEPRESS_SSR__) return + + const { VidstackPlayer, VidstackPlayerLayout } = await import( + 'vidstack/global/player' + ) + + const options: VidstackPlayerConfig = { + target: vidstack.value!, + crossOrigin: true, + poster: props.poster, + title: props.title, + ...props.player, + layout: new VidstackPlayerLayout({ + colorScheme: props.darkmode ? 'dark' : 'light', + thumbnails: props.thumbnails, + translations: locale.value, + ...props.layout, + }), + } + + options.src = isString(props.src) + ? getLink(props.src) + : isArray(props.src) + ? props.src.map((src) => (isString(src) ? getLink(src) : src)) + : props.src + + if (props.tracks.length) options.tracks = props.tracks + + player = await VidstackPlayer.create(options) + + player.addEventListener('provider-change', () => { + if (player!.provider?.type === 'hls' && HLS_JS_INSTALLED) + player!.provider.library = (() => + import( + /* webpackChunkName: "hls" */ 'hls.js/dist/hls.min.js' + )) as HLSConstructorLoader + else if (player!.provider?.type === 'dash' && DASHJS_INSTALLED) + player!.provider.library = (() => + import( + /* webpackChunkName: "dashjs" */ 'dashjs' + )) as DASHNamespaceLoader + }) + }) + + onBeforeUnmount(() => { + player?.destroy() + }) + + return (): VNode => h('div', { ref: vidstack }) + }, +}) diff --git a/plugins/features/plugin-media/src/client/components/index.ts b/plugins/features/plugin-media/src/client/components/index.ts new file mode 100644 index 0000000000..f6ae2de74b --- /dev/null +++ b/plugins/features/plugin-media/src/client/components/index.ts @@ -0,0 +1,3 @@ +export * from './ArtPlayer.js' +export * from './BiliBili.js' +export * from './VidStack.js' diff --git a/plugins/features/plugin-media/src/client/composables/index.ts b/plugins/features/plugin-media/src/client/composables/index.ts new file mode 100644 index 0000000000..0fc6125277 --- /dev/null +++ b/plugins/features/plugin-media/src/client/composables/index.ts @@ -0,0 +1 @@ +export * from "./useSize.js"; diff --git a/plugins/features/plugin-media/src/client/composables/useSize.ts b/plugins/features/plugin-media/src/client/composables/useSize.ts new file mode 100644 index 0000000000..c23fc8eef9 --- /dev/null +++ b/plugins/features/plugin-media/src/client/composables/useSize.ts @@ -0,0 +1,68 @@ +import { isString } from '@vuepress/helper/client' +import type { MaybeRef } from '@vueuse/core' +import { useEventListener } from '@vueuse/core' +import type { Ref, ShallowRef } from 'vue' +import { computed, isRef, onMounted, ref, shallowRef, unref, watch } from 'vue' + +const getValue = (value: number | string): string => + isString(value) ? value : `${value}px` + +export interface SizeOptions { + width: number | string | undefined + height: number | string | undefined + ratio: number | string | undefined +} + +export interface SizeInfo { + el: ShallowRef + width: Ref + height: Ref + resize: () => void +} + +export const useSize = ( + options: SizeOptions, + extraHeight: MaybeRef = 0, +): SizeInfo => { + const el = shallowRef() + const width = computed(() => getValue(unref(options.width) ?? '100%')) + const height = ref('auto') + + const getRadio = (ratio: number | string | undefined): number => { + if (isString(ratio)) { + const [radioWidth, radioHeight] = ratio.split(':') + const parsedRadio = Number(radioWidth) / Number(radioHeight) + + if (!Number.isNaN(parsedRadio)) return parsedRadio + } + + return typeof ratio === 'number' ? ratio : 16 / 9 + } + + const getHeight = (widthValue: number): string => { + const heightValue = unref(options.height) + const ratio = getRadio(unref(options.ratio)) + + return heightValue + ? getValue(heightValue) + : `${widthValue / ratio + unref(extraHeight)}px` + } + + const updateHeight = (): void => { + if (el.value) height.value = getHeight(el.value.clientWidth) + } + + onMounted(() => { + updateHeight() + if (isRef(extraHeight)) watch(extraHeight, updateHeight) + useEventListener('orientationchange', updateHeight) + useEventListener('resize', updateHeight) + }) + + return { + el, + width, + height, + resize: updateHeight, + } +} diff --git a/plugins/features/plugin-media/src/client/index.ts b/plugins/features/plugin-media/src/client/index.ts new file mode 100644 index 0000000000..166b13ec0e --- /dev/null +++ b/plugins/features/plugin-media/src/client/index.ts @@ -0,0 +1 @@ +export * from './components/index.js' diff --git a/plugins/features/plugin-media/src/client/styles/art-player.scss b/plugins/features/plugin-media/src/client/styles/art-player.scss new file mode 100644 index 0000000000..77f1db9cb3 --- /dev/null +++ b/plugins/features/plugin-media/src/client/styles/art-player.scss @@ -0,0 +1,3 @@ +.vp-artplayer { + text-align: center; +} diff --git a/plugins/features/plugin-media/src/client/styles/bili-bili.scss b/plugins/features/plugin-media/src/client/styles/bili-bili.scss new file mode 100644 index 0000000000..c3cf8afac0 --- /dev/null +++ b/plugins/features/plugin-media/src/client/styles/bili-bili.scss @@ -0,0 +1,15 @@ +.bilibili-desc a { + @media print { + display: block; + } +} + +.bilibili-iframe { + margin: 8px 0; + border: none; + border-radius: 8px; + + @media print { + display: none; + } +} diff --git a/plugins/features/plugin-media/src/client/styles/vidstack.scss b/plugins/features/plugin-media/src/client/styles/vidstack.scss new file mode 100644 index 0000000000..cf13c8c5b5 --- /dev/null +++ b/plugins/features/plugin-media/src/client/styles/vidstack.scss @@ -0,0 +1,15 @@ +.vds-audio-layout, +.vds-video-layout { + box-shadow: 2px 2px 10px 0 var(--vp-c-shadow); + + &, + &.light, + &.dark { + --default-brand: var(--vp-c-accent-bg); + --audio-bg: var(--vp-c-bg); + --default-controls-color: var(--vp-c-text-mute); + --default-focus-ring-color: var(--vp-c-accent-bg); + --default-border-radius: 8px; + --audio-border: 1px solid var(--vp-c-border); + } +} diff --git a/plugins/features/plugin-media/src/client/utils/getLink.ts b/plugins/features/plugin-media/src/client/utils/getLink.ts new file mode 100644 index 0000000000..3e4915bbd9 --- /dev/null +++ b/plugins/features/plugin-media/src/client/utils/getLink.ts @@ -0,0 +1,5 @@ +import { isLinkHttp } from "@vuepress/helper/client"; +import { withBase } from "vuepress/client"; + +export const getLink = (url: string): string => + isLinkHttp(url) ? url : withBase(url); diff --git a/plugins/features/plugin-media/src/client/utils/iframeAllow.ts b/plugins/features/plugin-media/src/client/utils/iframeAllow.ts new file mode 100644 index 0000000000..88b9ca1b3d --- /dev/null +++ b/plugins/features/plugin-media/src/client/utils/iframeAllow.ts @@ -0,0 +1,2 @@ +export const videoIframeAllow = + "accelerometer; autoplay; clipboard-write; encrypted-media; fullscreen; gyroscope; picture-in-picture"; diff --git a/plugins/features/plugin-media/src/client/utils/index.ts b/plugins/features/plugin-media/src/client/utils/index.ts new file mode 100644 index 0000000000..7cc76e50fc --- /dev/null +++ b/plugins/features/plugin-media/src/client/utils/index.ts @@ -0,0 +1,3 @@ +export * from './iframeAllow.js' +export * from './getLink.js' +export * from './registerMse.js' diff --git a/plugins/features/plugin-media/src/client/utils/registerMse.ts b/plugins/features/plugin-media/src/client/utils/registerMse.ts new file mode 100644 index 0000000000..5ee41221f9 --- /dev/null +++ b/plugins/features/plugin-media/src/client/utils/registerMse.ts @@ -0,0 +1,97 @@ +declare const DASHJS_INSTALLED: boolean +declare const HLS_JS_INSTALLED: boolean +declare const MPEGTS_JS_INSTALLED: boolean + +export const SUPPORTED_VIDEO_TYPES = ['mp4', 'mp3', 'webm', 'ogg'] + +if (typeof DASHJS_INSTALLED !== 'undefined' && DASHJS_INSTALLED) + SUPPORTED_VIDEO_TYPES.push('mpd', 'dash') + +if (typeof HLS_JS_INSTALLED !== 'undefined' && HLS_JS_INSTALLED) + SUPPORTED_VIDEO_TYPES.push('m3u8', 'hls') + +if (typeof MPEGTS_JS_INSTALLED !== 'undefined' && MPEGTS_JS_INSTALLED) + SUPPORTED_VIDEO_TYPES.push('ts', 'flv') + +export const getTypeByUrl = (url: string): string => url.split('.').pop() ?? '' + +export const registerMseDash = async ( + mediaElement: HTMLMediaElement, + src: string, + onDestroy: (destroy: () => void) => void, + autoPlay = false, + startTime = 0, +): Promise => { + if (typeof DASHJS_INSTALLED !== 'undefined' && DASHJS_INSTALLED) { + const dashjs = (await import(/* webpackChunkName: "dashjs" */ 'dashjs')) + .default + + if (dashjs.supportsMediaSource()) { + const dashPlayer = dashjs.MediaPlayer().create() + + dashPlayer.initialize(mediaElement, src, autoPlay, startTime) + + onDestroy(() => { + dashPlayer.destroy() + }) + } + } +} + +export const registerMseFlv = async ( + mediaElement: HTMLMediaElement, + src: string, + onDestroy: (destroy: () => void) => void, +): Promise => { + if (typeof MPEGTS_JS_INSTALLED !== 'undefined' && MPEGTS_JS_INSTALLED) { + const mpegts = ( + await import( + /* webpackChunkName: "mpegts.js" */ 'mpegts.js/dist/mpegts.js' + ) + ).default + + if (mpegts.isSupported()) { + const flvPlayer = mpegts.createPlayer({ + type: 'flv', + url: src, + }) + + flvPlayer.attachMediaElement(mediaElement) + flvPlayer.load() + + onDestroy(() => { + flvPlayer.destroy() + }) + } + } +} + +export const registerMseHls = async ( + mediaElement: HTMLMediaElement, + src: string, + onDestroy: (destroy: () => void) => void, +): Promise => { + if ( + mediaElement.canPlayType('application/x-mpegURL') || + mediaElement.canPlayType('application/vnd.apple.mpegURL') + ) { + mediaElement.src = src + } else if (typeof HLS_JS_INSTALLED !== 'undefined' && HLS_JS_INSTALLED) { + const HLS = ( + await import(/* webpackChunkName: "hls.js" */ 'hls.js/dist/hls.min.js') + ).default + + if (HLS.isSupported()) { + const hlsInstance = new HLS() + + hlsInstance.attachMedia(mediaElement) + hlsInstance.on(HLS.Events.MEDIA_ATTACHED, () => { + hlsInstance.loadSource(src) + }) + + onDestroy(() => { + hlsInstance.destroy() + }) + } + } +} diff --git a/plugins/features/plugin-media/src/node/getDefine.ts b/plugins/features/plugin-media/src/node/getDefine.ts new file mode 100644 index 0000000000..f7f1e38be1 --- /dev/null +++ b/plugins/features/plugin-media/src/node/getDefine.ts @@ -0,0 +1,52 @@ +import { getFullLocaleConfig } from '@vuepress/helper' +import type { App } from 'vuepress/core' + +import { vidstackLocaleInfo } from './locales/index.js' +import type { MediaPluginOptions } from './options.js' +import { isInstalled } from './utils.js' + +export const getDefine = + (options: MediaPluginOptions): ((app: App) => Record) => + (app) => { + const result: Record = {} + + if (options.artplayer || options.vidstack) { + result.DASHJS_INSTALLED = isInstalled('dashjs') + result.HLS_JS_INSTALLED = isInstalled('hls.js') + } + + if (options.artplayer) { + result.ART_PLAYER_OPTIONS = { + fullscreen: true, + playbackRate: true, + setting: true, + ...componentOptions.artPlayer, + } + result.MPEGTS_JS_INSTALLED = isInstalled('mpegts.js') + } + + if (options.pdf) { + result.PDF_LOCALES = getFullLocaleConfig({ + app, + name: 'pdf', + default: pdfLocaleInfo, + config: locales.pdf, + }) + result.PDFJS_URL = + typeof componentOptions.pdf?.pdfjs === 'string' + ? componentOptions.pdf.pdfjs + : componentOptions.pdf?.pdfjs === false + ? null + : 'https://theme-hope-assets.vuejs.press/pdfjs/' + } + + if (options.vidstack) + result.VIDSTACK_LOCALES = getFullLocaleConfig({ + app, + name: 'vidstack', + default: vidstackLocaleInfo, + config: locales.vidstack, + }) + + return result + } diff --git a/plugins/features/plugin-media/src/node/index.ts b/plugins/features/plugin-media/src/node/index.ts new file mode 100644 index 0000000000..334250e5a6 --- /dev/null +++ b/plugins/features/plugin-media/src/node/index.ts @@ -0,0 +1,2 @@ +export * from './mediaPlugin.js' +export type * from './options.js' diff --git a/plugins/features/plugin-media/src/node/locales/index.ts b/plugins/features/plugin-media/src/node/locales/index.ts new file mode 100644 index 0000000000..2415b895b9 --- /dev/null +++ b/plugins/features/plugin-media/src/node/locales/index.ts @@ -0,0 +1 @@ +export * from './vidstack.js' diff --git a/plugins/features/plugin-media/src/node/locales/vidstack.ts b/plugins/features/plugin-media/src/node/locales/vidstack.ts new file mode 100644 index 0000000000..6508677d60 --- /dev/null +++ b/plugins/features/plugin-media/src/node/locales/vidstack.ts @@ -0,0 +1,1068 @@ +import type { DefaultLocaleInfo } from '@vuepress/helper' + +import type { VidstackLocaleData } from '../../shared/index.js' + +export const vidstackLocaleInfo: DefaultLocaleInfo = [ + [['en', 'en-US'], {}], + [ + ['zh', 'zh-CN', 'zh-Hans'], + { + 'Play': '播放', + 'Pause': '暂停', + 'Enter Fullscreen': '进入全屏', + 'Exit Fullscreen': '退出全屏', + 'Enter PiP': '进入画中画', + 'Exit PiP': '退出画中画', + 'Closed-Captions On': '开启字幕', + 'Closed-Captions Off': '关闭字幕', + 'Mute': '静音', + 'Volume': '音量', + 'Seek Forward': '快进', + 'Seek Backward': '快退', + 'Announcements': '公告', + 'Accessibility': '无障碍', + 'Audio': '音频', + 'Auto': '自动', + 'Boost': '增强', + 'Captions': '字幕', + 'Caption Styles': '字幕样式', + 'Captions look like this': '字幕样式', + 'Chapters': '章节', + 'Connected': '已连接', + 'Continue': '继续', + 'Connecting': '连接中', + 'Default': '默认', + 'Disabled': '已禁用', + 'Disconnected': '已断开连接', + 'Display Background': '显示背景', + 'Download': '下载', + 'Font': '字体', + 'Family': '字体', + 'Fullscreen': '全屏', + 'Keyboard Animations': '键盘动画', + 'LIVE': '直播', + 'Loop': '循环', + 'Normal': '正常', + 'Off': '关闭', + 'Playback': '播放', + 'PiP': '画中画', + 'Quality': '画质', + 'Replay': '重播', + 'Reset': '重置', + 'Seek': '搜索', + 'Settings': '设置', + 'Skip To Live': '跳转到直播', + 'Speed': '速度', + 'Size': '大小', + 'Color': '颜色', + 'Opacity': '透明度', + 'Shadow': '阴影', + 'Text': '文本', + 'Text Background': '文本背景', + 'Track': '轨道', + 'Unmute': '取消静音', + }, + ], + [ + ['zh-tw', 'zh-Hant'], + { + 'Play': '播放', + 'Pause': '暫停', + 'Enter Fullscreen': '進入全螢幕', + 'Exit Fullscreen': '退出全螢幕', + 'Enter PiP': '進入畫中畫', + 'Exit PiP': '退出畫中畫', + 'Closed-Captions On': '開啟字幕', + 'Closed-Captions Off': '關閉字幕', + 'Mute': '靜音', + 'Volume': '音量', + 'Seek Forward': '快進', + 'Seek Backward': '快退', + 'Announcements': '公告', + 'Accessibility': '無障礙', + 'Audio': '音頻', + 'Auto': '自動', + 'Boost': '增強', + 'Captions': '字幕', + 'Caption Styles': '字幕樣式', + 'Captions look like this': '字幕樣式', + 'Chapters': '章節', + 'Connected': '已連接', + 'Continue': '繼續', + 'Connecting': '連接中', + 'Default': '默認', + 'Disabled': '已禁用', + 'Disconnected': '已斷開連接', + 'Display Background': '顯示背景', + 'Download': '下載', + 'Font': '字體', + 'Family': '字體', + 'Fullscreen': '全螢幕', + 'Keyboard Animations': '鍵盤動畫', + 'LIVE': '直播', + 'Loop': '循環', + 'Normal': '正常', + 'Off': '關閉', + 'Playback': '播放', + 'PiP': '畫中畫', + 'Quality': '畫質', + 'Replay': '重播', + 'Reset': '重置', + 'Seek': '搜索', + 'Settings': '設置', + 'Skip To Live': '跳轉到直播', + 'Speed': '速度', + 'Size': '大小', + 'Color': '顏色', + 'Opacity': '透明度', + 'Shadow': '陰影', + 'Text': '文本', + 'Text Background': '文本背景', + 'Track': '軌道', + 'Unmute': '取消靜音', + }, + ], + [ + ['de'], + { + 'Play': 'Wiedergabe', + 'Pause': 'Pause', + 'Enter Fullscreen': 'Vollbildmodus aktivieren', + 'Exit Fullscreen': 'Vollbildmodus beenden', + 'Enter PiP': 'Bild-in-Bild-Modus aktivieren', + 'Exit PiP': 'Bild-in-Bild-Modus beenden', + 'Closed-Captions On': 'Untertitel einblenden', + 'Closed-Captions Off': 'Untertitel ausblenden', + 'Mute': 'Stummschalten', + 'Volume': 'Lautstärke', + 'Seek Forward': 'Vorspulen', + 'Seek Backward': 'Zurückspulen', + 'Announcements': 'Ankündigungen', + 'Accessibility': 'Barrierefreiheit', + 'Audio': 'Audio', + 'Auto': 'Automatisch', + 'Boost': 'Boost', + 'Captions': 'Untertitel', + 'Caption Styles': 'Untertitel-Stile', + 'Captions look like this': 'Untertitel-Stile', + 'Chapters': 'Kapitel', + 'Connected': 'Verbunden', + 'Continue': 'Weiter', + 'Connecting': 'Verbinden', + 'Default': 'Standard', + 'Disabled': 'Deaktiviert', + 'Disconnected': 'Getrennt', + 'Display Background': 'Hintergrund anzeigen', + 'Download': 'Herunterladen', + 'Font': 'Schriftart', + 'Family': 'Schriftfamilie', + 'Fullscreen': 'Vollbild', + 'Keyboard Animations': 'Tastaturanimationen', + 'LIVE': 'LIVE', + 'Loop': 'Wiederholen', + 'Normal': 'Normal', + 'Off': 'Aus', + 'Playback': 'Wiedergabe', + 'PiP': 'Bild-in-Bild', + 'Quality': 'Qualität', + 'Replay': 'Wiederholen', + 'Reset': 'Zurücksetzen', + 'Seek': 'Suchen', + 'Settings': 'Einstellungen', + 'Skip To Live': 'Zum Live-Modus wechseln', + 'Speed': 'Geschwindigkeit', + 'Size': 'Größe', + 'Color': 'Farbe', + 'Opacity': 'Deckkraft', + 'Shadow': 'Schatten', + 'Text': 'Text', + 'Text Background': 'Text-Hintergrund', + 'Track': 'Spur', + 'Unmute': 'Ton einschalten', + }, + ], + [ + ['vi'], + { + 'Play': 'Phát', + 'Pause': 'Tạm dừng', + 'Enter Fullscreen': 'Mở toàn màn hình', + 'Exit Fullscreen': 'Thoát toàn màn hình', + 'Enter PiP': 'Vào chế độ hình trong hình', + 'Exit PiP': 'Thoát chế độ hình trong hình', + 'Closed-Captions On': 'Bật phụ đề', + 'Closed-Captions Off': 'Tắt phụ đề', + 'Mute': 'Tắt tiếng', + 'Volume': 'Âm lượng', + 'Seek Forward': 'Chuyển tiến', + 'Seek Backward': 'Chuyển lùi', + 'Announcements': 'Thông báo', + 'Accessibility': 'Truy cập', + 'Audio': 'Âm thanh', + 'Auto': 'Tự động', + 'Boost': 'Tăng cường', + 'Captions': 'Phụ đề', + 'Caption Styles': 'Kiểu phụ đề', + 'Captions look like this': 'Kiểu phụ đề', + 'Chapters': 'Chương', + 'Connected': 'Đã kết nối', + 'Continue': 'Tiếp tục', + 'Connecting': 'Đang kết nối', + 'Default': 'Mặc định', + 'Disabled': 'Đã tắt', + 'Disconnected': 'Đã ngắt kết nối', + 'Display Background': 'Hiển thị nền', + 'Download': 'Tải xuống', + 'Font': 'Phông chữ', + 'Family': 'Phông chữ', + 'Fullscreen': 'Toàn màn hình', + 'Keyboard Animations': 'Hiệu ứng bàn phím', + 'LIVE': 'TRỰC TIẾP', + 'Loop': 'Lặp lại', + 'Normal': 'Bình thường', + 'Off': 'Tắt', + 'Playback': 'Phát', + 'PiP': 'Hình trong hình', + 'Quality': 'Chất lượng', + 'Replay': 'Phát lại', + 'Reset': 'Đặt lại', + 'Seek': 'Tìm kiếm', + 'Settings': 'Cài đặt', + 'Skip To Live': 'Chuyển đến trực tiếp', + 'Speed': 'Tốc độ', + 'Size': 'Kích thước', + 'Color': 'Màu sắc', + 'Opacity': 'Độ mờ', + 'Shadow': 'Bóng', + 'Text': 'Văn bản', + 'Text Background': 'Nền văn bản', + 'Track': 'Dấu vết', + 'Unmute': 'Bật tiếng', + }, + ], + [ + ['uk'], + { + 'Play': 'Відтворити', + 'Pause': 'Пауза', + 'Enter Fullscreen': 'Увійти в повноекранний режим', + 'Exit Fullscreen': 'Вийти з повноекранного режиму', + 'Enter PiP': 'Увійти в режим картинка-в-картинці', + 'Exit PiP': 'Вийти з режиму картинка-в-картинці', + 'Closed-Captions On': 'Увімкнути субтитри', + 'Closed-Captions Off': 'Вимкнути субтитри', + 'Mute': 'Вимкнути звук', + 'Volume': 'Гучність', + 'Seek Forward': 'Прискорити', + 'Seek Backward': 'Прокрутити назад', + 'Announcements': 'Оголошення', + 'Accessibility': 'Доступність', + 'Audio': 'Аудіо', + 'Auto': 'Автоматично', + 'Boost': 'Підвищити', + 'Captions': 'Субтитри', + 'Caption Styles': 'Стилі субтитрів', + 'Captions look like this': 'Стилі субтитрів', + 'Chapters': 'Розділи', + 'Connected': 'Підключено', + 'Continue': 'Продовжити', + 'Connecting': 'Підключення', + 'Default': 'За замовчуванням', + 'Disabled': 'Вимкнено', + 'Disconnected': 'Відключено', + 'Display Background': 'Показати фон', + 'Download': 'Завантажити', + 'Font': 'Шрифт', + 'Family': 'Сімейство шрифтів', + 'Fullscreen': 'Повноекранний режим', + 'Keyboard Animations': 'Анімація клавіатури', + 'LIVE': 'Наживо', + 'Loop': 'Повторювати', + 'Normal': 'Звичайний', + 'Off': 'Вимкнено', + 'Playback': 'Відтворення', + 'PiP': 'Картинка-в-картинці', + 'Quality': 'Якість', + 'Replay': 'Повторити', + 'Reset': 'Скинути', + 'Seek': 'Пошук', + 'Settings': 'Налаштування', + 'Skip To Live': 'Перейти до прямого ефіру', + 'Speed': 'Швидкість', + 'Size': 'Розмір', + 'Color': 'Колір', + 'Opacity': 'Непрозорість', + 'Shadow': 'Тінь', + 'Text': 'Текст', + 'Text Background': 'Фон тексту', + 'Track': 'Трек', + 'Unmute': 'Увімкнути звук', + }, + ], + [ + ['ru'], + { + 'Play': 'Воспроизвести', + 'Pause': 'Пауза', + 'Enter Fullscreen': 'Войти в полноэкранный режим', + 'Exit Fullscreen': 'Выйти из полноэкранного режима', + 'Enter PiP': 'Войти в режим картинка в картинке', + 'Exit PiP': 'Выйти из режима картинка в картинке', + 'Closed-Captions On': 'Включить субтитры', + 'Closed-Captions Off': 'Выключить субтитры', + 'Mute': 'Выключить звук', + 'Volume': 'Громкость', + 'Seek Forward': 'Ускорить', + 'Seek Backward': 'Прокрутить назад', + 'Announcements': 'Объявления', + 'Accessibility': 'Доступность', + 'Audio': 'Аудио', + 'Auto': 'Автоматически', + 'Boost': 'Усилить', + 'Captions': 'Субтитры', + 'Caption Styles': 'Стили субтитров', + 'Captions look like this': 'Стили субтитров', + 'Chapters': 'Главы', + 'Connected': 'Подключено', + 'Continue': 'Продолжить', + 'Connecting': 'Подключение', + 'Default': 'По умолчанию', + 'Disabled': 'Отключено', + 'Disconnected': 'Отключено', + 'Display Background': 'Показать фон', + 'Download': 'Скачать', + 'Font': 'Шрифт', + 'Family': 'Семейство шрифтов', + 'Fullscreen': 'Полноэкранный режим', + 'Keyboard Animations': 'Анимации клавиатуры', + 'LIVE': 'В прямом эфире', + 'Loop': 'Повторять', + 'Normal': 'Обычный', + 'Off': 'Отключено', + 'Playback': 'Воспроизведение', + 'PiP': 'Картинка в картинке', + 'Quality': 'Качество', + 'Replay': 'Повторить', + 'Reset': 'Сбросить', + 'Seek': 'Поиск', + 'Settings': 'Настройки', + 'Skip To Live': 'Перейти к прямому эфиру', + 'Speed': 'Скорость', + 'Size': 'Размер', + 'Color': 'Цвет', + 'Opacity': 'Непрозрачность', + 'Shadow': 'Тень', + 'Text': 'Текст', + 'Text Background': 'Фон текста', + 'Track': 'Трек', + 'Unmute': 'Включить звук', + }, + ], + [ + ['br'], + { + 'Play': 'Reproduzir', + 'Pause': 'Pausar', + 'Enter Fullscreen': 'Entrar em tela cheia', + 'Exit Fullscreen': 'Sair da tela cheia', + 'Enter PiP': 'Entrar em PiP', + 'Exit PiP': 'Sair de PiP', + 'Closed-Captions On': 'Ativar legendas', + 'Closed-Captions Off': 'Desativar legendas', + 'Mute': 'Mudo', + 'Volume': 'Volume', + 'Seek Forward': 'Avançar', + 'Seek Backward': 'Retroceder', + 'Announcements': 'Anúncios', + 'Accessibility': 'Acessibilidade', + 'Audio': 'Áudio', + 'Auto': 'Automático', + 'Boost': 'Aumentar', + 'Captions': 'Legendas', + 'Caption Styles': 'Estilos de legenda', + 'Captions look like this': 'Estilos de legenda', + 'Chapters': 'Capítulos', + 'Connected': 'Conectado', + 'Continue': 'Continuar', + 'Connecting': 'Conectando', + 'Default': 'Padrão', + 'Disabled': 'Desativado', + 'Disconnected': 'Desconectado', + 'Display Background': 'Mostrar fundo', + 'Download': 'Baixar', + 'Font': 'Fonte', + 'Family': 'Família', + 'Fullscreen': 'Tela cheia', + 'Keyboard Animations': 'Animações do teclado', + 'LIVE': 'AO VIVO', + 'Loop': 'Repetir', + 'Normal': 'Normal', + 'Off': 'Desligado', + 'Playback': 'Reprodução', + 'PiP': 'PiP', + 'Quality': 'Qualidade', + 'Replay': 'Repetir', + 'Reset': 'Redefinir', + 'Seek': 'Buscar', + 'Settings': 'Configurações', + 'Skip To Live': 'Pular para ao vivo', + 'Speed': 'Velocidade', + 'Size': 'Tamanho', + 'Color': 'Cor', + 'Opacity': 'Opacidade', + 'Shadow': 'Sombra', + 'Text': 'Texto', + 'Text Background': 'Fundo do texto', + 'Track': 'Trilha', + 'Unmute': 'Ativar som', + }, + ], + [ + ['p'], + { + 'Play': 'Odtwórz', + 'Pause': 'Pauza', + 'Enter Fullscreen': 'Wejdź na pełny ekran', + 'Exit Fullscreen': 'Wyjdź z pełnego ekranu', + 'Enter PiP': 'Wejdź w tryb obraz w obrazie', + 'Exit PiP': 'Wyjdź z trybu obraz w obrazie', + 'Closed-Captions On': 'Włącz napisy', + 'Closed-Captions Off': 'Wyłącz napisy', + 'Mute': 'Wycisz', + 'Volume': 'Głośność', + 'Seek Forward': 'Przewiń do przodu', + 'Seek Backward': 'Przewiń do tyłu', + 'Announcements': 'Ogłoszenia', + 'Accessibility': 'Dostępność', + 'Audio': 'Audio', + 'Auto': 'Automatycznie', + 'Boost': 'Wzmocnij', + 'Captions': 'Napisy', + 'Caption Styles': 'Style napisów', + 'Captions look like this': 'Style napisów', + 'Chapters': 'Rozdziały', + 'Connected': 'Połączono', + 'Continue': 'Kontynuuj', + 'Connecting': 'Łączenie', + 'Default': 'Domyślne', + 'Disabled': 'Wyłączone', + 'Disconnected': 'Rozłączono', + 'Display Background': 'Pokaż tło', + 'Download': 'Pobierz', + 'Font': 'Czcionka', + 'Family': 'Rodzina', + 'Fullscreen': 'Pełny ekran', + 'Keyboard Animations': 'Animacje klawiatury', + 'LIVE': 'NA ŻYWO', + 'Loop': 'Pętla', + 'Normal': 'Normalny', + 'Off': 'Wyłącz', + 'Playback': 'Odtwarzanie', + 'PiP': 'Obraz w obrazie', + 'Quality': 'Jakość', + 'Replay': 'Powtórz', + 'Reset': 'Resetuj', + 'Seek': 'Szukaj', + 'Settings': 'Ustawienia', + 'Skip To Live': 'Przejdź na żywo', + 'Speed': 'Prędkość', + 'Size': 'Rozmiar', + 'Color': 'Kolor', + 'Opacity': 'Przezroczystość', + 'Shadow': 'Cień', + 'Text': 'Tekst', + 'Text Background': 'Tło tekstu', + 'Track': 'Ścieżka', + 'Unmute': 'Włącz dźwięk', + }, + ], + [ + ['sk'], + { + 'Play': 'Prehrať', + 'Pause': 'Pauza', + 'Enter Fullscreen': 'Prepnúť na celú obrazovku', + 'Exit Fullscreen': 'Opustiť celú obrazovku', + 'Enter PiP': 'Prepnúť na obraz v obraze', + 'Exit PiP': 'Opustiť obraz v obraze', + 'Closed-Captions On': 'Zapnúť titulky', + 'Closed-Captions Off': 'Vypnúť titulky', + 'Mute': 'Ztlmiť', + 'Volume': 'Hlasitosť', + 'Seek Forward': 'Posunúť vpred', + 'Seek Backward': 'Posunúť vzad', + 'Announcements': 'Oznámenia', + 'Accessibility': 'Prístupnosť', + 'Audio': 'Audio', + 'Auto': 'Automaticky', + 'Boost': 'Zvýšiť', + 'Captions': 'Titulky', + 'Caption Styles': 'Štýly titulkov', + 'Captions look like this': 'Štýly titulkov', + 'Chapters': 'Kapitoly', + 'Connected': 'Pripojené', + 'Continue': 'Pokračovať', + 'Connecting': 'Pripájanie', + 'Default': 'Predvolené', + 'Disabled': 'Zakázané', + 'Disconnected': 'Odpojené', + 'Display Background': 'Zobraziť pozadie', + 'Download': 'Stiahnuť', + 'Font': 'Písmo', + 'Family': 'Rodina', + 'Fullscreen': 'Celá obrazovka', + 'Keyboard Animations': 'Klávesové animácie', + 'LIVE': 'ŽIVÉ', + 'Loop': 'Opakovať', + 'Normal': 'Normálne', + 'Off': 'Vypnuté', + 'Playback': 'Prehrávanie', + 'PiP': 'Obraz v obraze', + 'Quality': 'Kvalita', + 'Replay': 'Zopakovať', + 'Reset': 'Obnoviť', + 'Seek': 'Hľadať', + 'Settings': 'Nastavenia', + 'Skip To Live': 'Preskočiť na živý prenos', + 'Speed': 'Rýchlosť', + 'Size': 'Veľkosť', + 'Color': 'Farba', + 'Opacity': 'Priehľadnosť', + 'Shadow': 'Tieň', + 'Text': 'Text', + 'Text Background': 'Pozadie textu', + 'Track': 'Stopa', + 'Unmute': 'Zapnúť zvuk', + }, + ], + [ + ['fr'], + { + 'Play': 'Lecture', + 'Pause': 'Pause', + 'Enter Fullscreen': 'Passer en plein écran', + 'Exit Fullscreen': 'Quitter le plein écran', + 'Enter PiP': "Passer en mode image dans l'image", + 'Exit PiP': "Quitter le mode image dans l'image", + 'Closed-Captions On': 'Activer les sous-titres', + 'Closed-Captions Off': 'Désactiver les sous-titres', + 'Mute': 'Muet', + 'Volume': 'Volume', + 'Seek Forward': 'Avancer', + 'Seek Backward': 'Reculer', + 'Announcements': 'Annonces', + 'Accessibility': 'Accessibilité', + 'Audio': 'Audio', + 'Auto': 'Automatique', + 'Boost': 'Amplifier', + 'Captions': 'Sous-titres', + 'Caption Styles': 'Styles de sous-titres', + 'Captions look like this': 'Styles de sous-titres', + 'Chapters': 'Chapitres', + 'Connected': 'Connecté', + 'Continue': 'Continuer', + 'Connecting': 'Connexion', + 'Default': 'Par défaut', + 'Disabled': 'Désactivé', + 'Disconnected': 'Déconnecté', + 'Display Background': "Afficher l'arrière-plan", + 'Download': 'Télécharger', + 'Font': 'Police', + 'Family': 'Famille', + 'Fullscreen': 'Plein écran', + 'Keyboard Animations': 'Animations du clavier', + 'LIVE': 'EN DIRECT', + 'Loop': 'Boucle', + 'Normal': 'Normal', + 'Off': 'Désactivé', + 'Playback': 'Lecture', + 'PiP': "Image dans l'image", + 'Quality': 'Qualité', + 'Replay': 'Rejouer', + 'Reset': 'Réinitialiser', + 'Seek': 'Rechercher', + 'Settings': 'Paramètres', + 'Skip To Live': 'Aller en direct', + 'Speed': 'Vitesse', + 'Size': 'Taille', + 'Color': 'Couleur', + 'Opacity': 'Opacité', + 'Shadow': 'Ombre', + 'Text': 'Texte', + 'Text Background': 'Arrière-plan du texte', + 'Track': 'Piste', + 'Unmute': 'Activer le son', + }, + ], + [ + ['es'], + { + 'Play': 'Reproducir', + 'Pause': 'Pausa', + 'Enter Fullscreen': 'Entrar en pantalla completa', + 'Exit Fullscreen': 'Salir de pantalla completa', + 'Enter PiP': 'Entrar en modo imagen en imagen', + 'Exit PiP': 'Salir de modo imagen en imagen', + 'Closed-Captions On': 'Activar subtítulos', + 'Closed-Captions Off': 'Desactivar subtítulos', + 'Mute': 'Silenciar', + 'Volume': 'Volumen', + 'Seek Forward': 'Avanzar', + 'Seek Backward': 'Retroceder', + 'Announcements': 'Anuncios', + 'Accessibility': 'Accesibilidad', + 'Audio': 'Audio', + 'Auto': 'Automático', + 'Boost': 'Aumentar', + 'Captions': 'Subtítulos', + 'Caption Styles': 'Estilos de subtítulos', + 'Captions look like this': 'Estilos de subtítulos', + 'Chapters': 'Capítulos', + 'Connected': 'Conectado', + 'Continue': 'Continuar', + 'Connecting': 'Conectando', + 'Default': 'Predeterminado', + 'Disabled': 'Desactivado', + 'Disconnected': 'Desconectado', + 'Display Background': 'Mostrar fondo', + 'Download': 'Descargar', + 'Font': 'Fuente', + 'Family': 'Familia', + 'Fullscreen': 'Pantalla completa', + 'Keyboard Animations': 'Animaciones de teclado', + 'LIVE': 'EN DIRECTO', + 'Loop': 'Repetir', + 'Normal': 'Normal', + 'Off': 'Desactivado', + 'Playback': 'Reproducción', + 'PiP': 'Imagen en imagen', + 'Quality': 'Calidad', + 'Replay': 'Repetir', + 'Reset': 'Restablecer', + 'Seek': 'Buscar', + 'Settings': 'Configuración', + 'Skip To Live': 'Ir en directo', + 'Speed': 'Velocidad', + 'Size': 'Tamaño', + 'Color': 'Color', + 'Opacity': 'Opacidad', + 'Shadow': 'Sombra', + 'Text': 'Texto', + 'Text Background': 'Fondo de texto', + 'Track': 'Pista', + 'Unmute': 'Activar sonido', + }, + ], + [ + ['ja'], + { + 'Play': '再生', + 'Pause': '一時停止', + 'Enter Fullscreen': 'フルスクリーンに入る', + 'Exit Fullscreen': 'フルスクリーンを終了', + 'Enter PiP': 'PiPモードに入る', + 'Exit PiP': 'PiPモードを終了', + 'Closed-Captions On': '字幕をオンにする', + 'Closed-Captions Off': '字幕をオフにする', + 'Mute': 'ミュート', + 'Volume': '音量', + 'Seek Forward': '早送り', + 'Seek Backward': '巻き戻し', + 'Announcements': 'お知らせ', + 'Accessibility': 'アクセシビリティ', + 'Audio': 'オーディオ', + 'Auto': '自動', + 'Boost': 'ブースト', + 'Captions': '字幕', + 'Caption Styles': '字幕スタイル', + 'Captions look like this': '字幕スタイル', + 'Chapters': 'チャプター', + 'Connected': '接続済み', + 'Continue': '続行', + 'Connecting': '接続中', + 'Default': 'デフォルト', + 'Disabled': '無効', + 'Disconnected': '切断済み', + 'Display Background': '背景を表示', + 'Download': 'ダウンロード', + 'Font': 'フォント', + 'Family': 'ファミリー', + 'Fullscreen': 'フルスクリーン', + 'Keyboard Animations': 'キーボードアニメーション', + 'LIVE': 'ライブ', + 'Loop': 'ループ', + 'Normal': '通常', + 'Off': 'オフ', + 'Playback': '再生', + 'PiP': 'PiP', + 'Quality': '画質', + 'Replay': 'リプレイ', + 'Reset': 'リセット', + 'Seek': '検索', + 'Settings': '設定', + 'Skip To Live': 'ライブにスキップ', + 'Speed': '速度', + 'Size': 'サイズ', + 'Color': '色', + 'Opacity': '不透明度', + 'Shadow': '影', + 'Text': 'テキスト', + 'Text Background': 'テキストの背景', + 'Track': 'トラック', + 'Unmute': 'ミュート解除', + }, + ], + [ + ['tr'], + { + 'Play': 'Oynat', + 'Pause': 'Duraklat', + 'Enter Fullscreen': 'Tam Ekrana Gir', + 'Exit Fullscreen': 'Tam Ekrandan Çık', + 'Enter PiP': "PiP'ye Gir", + 'Exit PiP': "PiP'den Çık", + 'Closed-Captions On': 'Altyazıları Aç', + 'Closed-Captions Off': 'Altyazıları Kapat', + 'Mute': 'Sessiz', + 'Volume': 'Ses', + 'Seek Forward': 'İleri Sar', + 'Seek Backward': 'Geri Sar', + 'Announcements': 'Duyurular', + 'Accessibility': 'Erişilebilirlik', + 'Audio': 'Ses', + 'Auto': 'Otomatik', + 'Boost': 'Artır', + 'Captions': 'Altyazılar', + 'Caption Styles': 'Altyazı Stilleri', + 'Captions look like this': 'Altyazı Stilleri', + 'Chapters': 'Bölümler', + 'Connected': 'Bağlı', + 'Continue': 'Devam', + 'Connecting': 'Bağlanıyor', + 'Default': 'Varsayılan', + 'Disabled': 'Devre Dışı', + 'Disconnected': 'Bağlantı Kesildi', + 'Display Background': 'Arka Planı Göster', + 'Download': 'İndir', + 'Font': 'Yazı Tipi', + 'Family': 'Aile', + 'Fullscreen': 'Tam Ekran', + 'Keyboard Animations': 'Klavye Animasyonları', + 'LIVE': 'CANLI', + 'Loop': 'Döngü', + 'Normal': 'Normal', + 'Off': 'Kapalı', + 'Playback': 'Oynatma', + 'PiP': 'PiP', + 'Quality': 'Kalite', + 'Replay': 'Tekrar Oynat', + 'Reset': 'Sıfırla', + 'Seek': 'Ara', + 'Settings': 'Ayarlar', + 'Skip To Live': 'Canlıya Geç', + 'Speed': 'Hız', + 'Size': 'Boyut', + 'Color': 'Renk', + 'Opacity': 'Opaklık', + 'Shadow': 'Gölge', + 'Text': 'Metin', + 'Text Background': 'Metin Arka Planı', + 'Track': 'Parça', + 'Unmute': 'Sesi Aç', + }, + ], + [ + ['ko'], + { + 'Play': '재생', + 'Pause': '일시정지', + 'Enter Fullscreen': '전체화면으로 들어가기', + 'Exit Fullscreen': '전체화면에서 나가기', + 'Enter PiP': 'PiP 모드로 들어가기', + 'Exit PiP': 'PiP 모드에서 나가기', + 'Closed-Captions On': '자막 켜기', + 'Closed-Captions Off': '자막 끄기', + 'Mute': '음소거', + 'Volume': '볼륨', + 'Seek Forward': '앞으로 이동', + 'Seek Backward': '뒤로 이동', + 'Announcements': '공지', + 'Accessibility': '접근성', + 'Audio': '오디오', + 'Auto': '자동', + 'Boost': '부스트', + 'Captions': '자막', + 'Caption Styles': '자막 스타일', + 'Captions look like this': '자막 스타일', + 'Chapters': '챕터', + 'Connected': '연결됨', + 'Continue': '계속', + 'Connecting': '연결 중', + 'Default': '기본값', + 'Disabled': '비활성화됨', + 'Disconnected': '연결 끊김', + 'Display Background': '배경 표시', + 'Download': '다운로드', + 'Font': '글꼴', + 'Family': '가족', + 'Fullscreen': '전체화면', + 'Keyboard Animations': '키보드 애니메이션', + 'LIVE': '실시간', + 'Loop': '반복', + 'Normal': '보통', + 'Off': '끔', + 'Playback': '재생', + 'Quality': '품질', + 'Replay': '다시 보기', + 'Reset': '재설정', + 'Seek': '검색', + 'Settings': '설정', + 'Skip To Live': '실시간으로 이동', + 'Speed': '속도', + 'Size': '크기', + 'Color': '색상', + 'Opacity': '불투명도', + 'Shadow': '그림자', + 'Text': '텍스트', + 'Text Background': '텍스트 배경', + 'Track': '트랙', + 'Unmute': '음소거 해제', + }, + ], + [ + ['fi'], + { + 'Play': 'Toista', + 'Pause': 'Tauko', + 'Enter Fullscreen': 'Siirry koko näytölle', + 'Exit Fullscreen': 'Poistu koko näytöltä', + 'Enter PiP': 'Siirry kuva kuvassa -tilaan', + 'Exit PiP': 'Poistu kuva kuvassa -tilasta', + 'Closed-Captions On': 'Ota tekstitykset käyttöön', + 'Closed-Captions Off': 'Poista tekstitykset käytöstä', + 'Mute': 'Mykistä', + 'Volume': 'Äänenvoimakkuus', + 'Seek Forward': 'Siirry eteenpäin', + 'Seek Backward': 'Siirry taaksepäin', + 'Announcements': 'Ilmoitukset', + 'Accessibility': 'Saavutettavuus', + 'Audio': 'Ääni', + 'Auto': 'Automaattinen', + 'Boost': 'Tehosta', + 'Captions': 'Tekstitykset', + 'Caption Styles': 'Tekstitystyyli', + 'Captions look like this': 'Tekstitystyyli', + 'Chapters': 'Luvut', + 'Connected': 'Yhdistetty', + 'Continue': 'Jatka', + 'Connecting': 'Yhdistetään', + 'Default': 'Oletus', + 'Disabled': 'Poistettu käytöstä', + 'Disconnected': 'Yhteys katkaistu', + 'Display Background': 'Näytä tausta', + 'Download': 'Lataa', + 'Font': 'Fontti', + 'Family': 'Perhe', + 'Fullscreen': 'Koko näyttö', + 'Keyboard Animations': 'Näppäin animaatiot', + 'LIVE': 'LIVE', + 'Loop': 'Toista uudelleen', + 'Normal': 'Normaali', + 'Off': 'Pois', + 'Playback': 'Toisto', + 'PiP': 'Kuva kuvassa', + 'Quality': 'Laatu', + 'Replay': 'Toista uudelleen', + 'Reset': 'Palauta', + 'Seek': 'Hae', + 'Settings': 'Asetukset', + 'Skip To Live': 'Siirry suorana', + 'Speed': 'Nopeus', + 'Size': 'Koko', + 'Color': 'Väri', + 'Opacity': 'Sameus', + 'Shadow': 'Varjo', + 'Text': 'Teksti', + 'Text Background': 'Tekstin tausta', + 'Track': 'Raita', + 'Unmute': 'Poista mykistys', + }, + ], + [ + ['hu'], + { + 'Play': 'Lejátszás', + 'Pause': 'Szünet', + 'Enter Fullscreen': 'Belépés a teljes képernyős módba', + 'Exit Fullscreen': 'Kilépés a teljes képernyős módból', + 'Enter PiP': 'Belépés a kép a képben módba', + 'Exit PiP': 'Kilépés a kép a képben módból', + 'Closed-Captions On': 'Feliratok bekapcsolása', + 'Closed-Captions Off': 'Feliratok kikapcsolása', + 'Mute': 'Némítás', + 'Volume': 'Hangerő', + 'Seek Forward': 'Előre ugrás', + 'Seek Backward': 'Visszalépés', + 'Announcements': 'Hirdetések', + 'Accessibility': 'Hozzáférhetőség', + 'Audio': 'Hang', + 'Auto': 'Automatikus', + 'Boost': 'Felerősítés', + 'Captions': 'Feliratok', + 'Caption Styles': 'Felirat stílusok', + 'Captions look like this': 'Felirat stílusok', + 'Chapters': 'Fejezetek', + 'Connected': 'Csatlakoztatva', + 'Continue': 'Folytatás', + 'Connecting': 'Csatlakozás', + 'Default': 'Alapértelmezett', + 'Disabled': 'Letiltva', + 'Disconnected': 'Nincs kapcsolat', + 'Display Background': 'Háttér megjelenítése', + 'Download': 'Letöltés', + 'Font': 'Betűtípus', + 'Family': 'Család', + 'Fullscreen': 'Teljes képernyő', + 'Keyboard Animations': 'Billentyűzet animációk', + 'LIVE': 'ÉLŐ', + 'Loop': 'Ismétlés', + 'Normal': 'Normál', + 'Off': 'Kikapcsolva', + 'Playback': 'Lejátszás', + 'PiP': 'Kép a képben', + 'Quality': 'Minőség', + 'Replay': 'Ismétlés', + 'Reset': 'Visszaállítás', + 'Seek': 'Keresés', + 'Settings': 'Beállítások', + 'Skip To Live': 'Ugrás az élő adásra', + 'Speed': 'Sebesség', + 'Size': 'Méret', + 'Color': 'Szín', + 'Opacity': 'Átlátszatlanság', + 'Shadow': 'Árnyék', + 'Text': 'Szöveg', + 'Text Background': 'Szöveg háttér', + 'Track': 'Sáv', + 'Unmute': 'Némítás feloldása', + }, + ], + [ + ['id'], + { + 'Play': 'Putar', + 'Pause': 'Jeda', + 'Enter Fullscreen': 'Masuk ke Layar Penuh', + 'Exit Fullscreen': 'Keluar dari Layar Penuh', + 'Enter PiP': 'Masuk ke Mode PiP', + 'Exit PiP': 'Keluar dari Mode PiP', + 'Closed-Captions On': 'Nyalakan Teks', + 'Closed-Captions Off': 'Matikan Teks', + 'Mute': 'Bisukan', + 'Volume': 'Volume', + 'Seek Forward': 'Cari Maju', + 'Seek Backward': 'Cari Mundur', + 'Announcements': 'Pengumuman', + 'Accessibility': 'Aksesibilitas', + 'Audio': 'Audio', + 'Auto': 'Otomatis', + 'Boost': 'Tingkatkan', + 'Captions': 'Teks', + 'Caption Styles': 'Gaya Teks', + 'Captions look like this': 'Gaya Teks', + 'Chapters': 'Bab', + 'Connected': 'Terhubung', + 'Continue': 'Lanjutkan', + 'Connecting': 'Menghubungkan', + 'Default': 'Default', + 'Disabled': 'Nonaktif', + 'Disconnected': 'Terputus', + 'Display Background': 'Tampilkan Latar Belakang', + 'Download': 'Unduh', + 'Font': 'Font', + 'Family': 'Keluarga', + 'Fullscreen': 'Layar Penuh', + 'Keyboard Animations': 'Animasi Keyboard', + 'LIVE': 'LANGSUNG', + 'Loop': 'Ulang', + 'Normal': 'Normal', + 'Off': 'Mati', + 'Playback': 'Putar', + 'PiP': 'PiP', + 'Quality': 'Kualitas', + 'Replay': 'Putar Ulang', + 'Reset': 'Atur Ulang', + 'Seek': 'Cari', + 'Settings': 'Pengaturan', + 'Skip To Live': 'Lewati ke Langsung', + 'Speed': 'Kecepatan', + 'Size': 'Ukuran', + 'Color': 'Warna', + 'Opacity': 'Ketidakjelasan', + 'Shadow': 'Bayangan', + 'Text': 'Teks', + 'Text Background': 'Latar Belakang Teks', + 'Track': 'Lacak', + 'Unmute': 'Bunyikan', + }, + ], + [ + ['nl'], + { + 'Play': 'Afspelen', + 'Pause': 'Pauze', + 'Enter Fullscreen': 'Volledig scherm', + 'Exit Fullscreen': 'Verlaat volledig scherm', + 'Enter PiP': 'Beeld-in-beeld', + 'Exit PiP': 'Verlaat beeld-in-beeld', + 'Closed-Captions On': 'Ondertiteling aan', + 'Closed-Captions Off': 'Ondertiteling uit', + 'Mute': 'Geluid uit', + 'Volume': 'Volume', + 'Seek Forward': 'Vooruit spoelen', + 'Seek Backward': 'Achteruit spoelen', + 'Announcements': 'Mededelingen', + 'Accessibility': 'Toegankelijkheid', + 'Audio': 'Audio', + 'Auto': 'Automatisch', + 'Boost': 'Versterken', + 'Captions': 'Ondertiteling', + 'Caption Styles': 'Ondertitelstijlen', + 'Captions look like this': 'Ondertitelstijlen', + 'Chapters': 'Hoofdstukken', + 'Connected': 'Verbonden', + 'Continue': 'Doorgaan', + 'Connecting': 'Verbinden', + 'Default': 'Standaard', + 'Disabled': 'Uitgeschakeld', + 'Disconnected': 'Verbroken', + 'Display Background': 'Achtergrond weergeven', + 'Download': 'Downloaden', + 'Font': 'Lettertype', + 'Family': 'Familie', + 'Fullscreen': 'Volledig scherm', + 'Keyboard Animations': 'Toetsenbordanimaties', + 'LIVE': 'LIVE', + 'Loop': 'Herhalen', + 'Normal': 'Normaal', + 'Off': 'Uit', + 'Playback': 'Afspelen', + 'PiP': 'Beeld-in-beeld', + 'Quality': 'Kwaliteit', + 'Replay': 'Opnieuw afspelen', + 'Reset': 'Resetten', + 'Seek': 'Zoeken', + 'Settings': 'Instellingen', + 'Skip To Live': 'Naar live gaan', + 'Speed': 'Snelheid', + 'Size': 'Grootte', + 'Color': 'Kleur', + 'Opacity': 'Dekking', + 'Shadow': 'Schaduw', + 'Text': 'Tekst', + 'Text Background': 'Tekstachtergrond', + 'Track': 'Track', + 'Unmute': 'Geluid aan', + }, + ], +] diff --git a/plugins/features/plugin-media/src/node/mediaPlugin.ts b/plugins/features/plugin-media/src/node/mediaPlugin.ts new file mode 100644 index 0000000000..51f27736ca --- /dev/null +++ b/plugins/features/plugin-media/src/node/mediaPlugin.ts @@ -0,0 +1,13 @@ +import type { Plugin } from 'vuepress/core' +import { getDirname, path } from 'vuepress/utils' +import type { MediaPluginOptions } from './options.js' + +const __dirname = import.meta.dirname || getDirname(import.meta.url) + +export const mediaPlugin = (options: MediaPluginOptions = {}): Plugin => ({ + name: '@vuepress/plugin-media', + + clientConfigFile: path.resolve(__dirname, '../client/config.js'), + + define: getDefine(options), +}) diff --git a/plugins/features/plugin-media/src/node/options.ts b/plugins/features/plugin-media/src/node/options.ts new file mode 100644 index 0000000000..f7d2a7abf2 --- /dev/null +++ b/plugins/features/plugin-media/src/node/options.ts @@ -0,0 +1,14 @@ +/** + * Options for @vuepress/plugin-media + */ +export interface MediaPluginOptions { + artplayer?: boolean + bilibili?: boolean + vidstack?: boolean + pdf?: + | boolean + | { + pdfjs?: string | false + } + pdfLocales?: Record +} diff --git a/plugins/features/plugin-media/src/node/utils.ts b/plugins/features/plugin-media/src/node/utils.ts new file mode 100644 index 0000000000..2d31e281b5 --- /dev/null +++ b/plugins/features/plugin-media/src/node/utils.ts @@ -0,0 +1,22 @@ +import { Logger, ensureEndingSlash, isModuleAvailable } from '@vuepress/helper' +import { getDirname, path } from 'vuepress/utils' + +const __dirname = getDirname(import.meta.url) + +export const COMPONENT_PKG: Record = { + ArtPlayer: ['artplayer'], + AudioPlayer: ['vidstack'], + VidStack: ['vidstack'], + VideoPlayer: ['vidstack'], +} + +export const CLIENT_FOLDER = ensureEndingSlash( + path.resolve(__dirname, '../client'), +) + +export const PLUGIN_NAME = 'vuepress-plugin-components' + +export const logger = new Logger(PLUGIN_NAME) + +export const isInstalled = (pkg: string): boolean => + isModuleAvailable(pkg, import.meta) diff --git a/plugins/features/plugin-media/src/shared/artplayer.ts b/plugins/features/plugin-media/src/shared/artplayer.ts new file mode 100644 index 0000000000..88e181e599 --- /dev/null +++ b/plugins/features/plugin-media/src/shared/artplayer.ts @@ -0,0 +1,16 @@ +import type { Option as ArtPlayerInitOptions } from 'artplayer' + +export type ArtPlayerOptions = Partial< + Omit< + ArtPlayerInitOptions, + | 'container' + | 'contextmenu' + | 'controls' + | 'customType' + | 'layers' + | 'plugins' + | 'settings' + | 'type' + | 'url' + > +> diff --git a/plugins/features/plugin-media/src/shared/index.ts b/plugins/features/plugin-media/src/shared/index.ts new file mode 100644 index 0000000000..003e1f0db5 --- /dev/null +++ b/plugins/features/plugin-media/src/shared/index.ts @@ -0,0 +1,3 @@ +export type * from './artplayer.js' +export type * from './locales.js' +export type * from './share.js' diff --git a/plugins/features/plugin-media/src/shared/locales.ts b/plugins/features/plugin-media/src/shared/locales.ts new file mode 100644 index 0000000000..d4d927df23 --- /dev/null +++ b/plugins/features/plugin-media/src/shared/locales.ts @@ -0,0 +1,18 @@ +import type { DefaultLayoutTranslations } from 'vidstack' + +export interface PDFLocaleData { + /** + * PDF hint text + * + * @description Only used if the browser does not support embedding PDF and no PDFJS URL is provided. + * [url] will be replaced by actual PDF link. + * + * PDF 提示文字 + * + * @description 只有在浏览器不支持嵌入 PDF 且没有提供 PDFJS URL 时才会使用 + * [url] 会被实际 PDF 链接替换 + */ + hint: string +} + +export type VidstackLocaleData = Partial diff --git a/plugins/features/plugin-media/src/shared/share.ts b/plugins/features/plugin-media/src/shared/share.ts new file mode 100644 index 0000000000..155c52b9e2 --- /dev/null +++ b/plugins/features/plugin-media/src/shared/share.ts @@ -0,0 +1,122 @@ +export type ShareAction = 'navigate' | 'open' | 'popup' | 'qrcode' + +export type BuiltInShareService = + | 'buffer' + | 'douban' + | 'email' + | 'evernote' + | 'facebook' + | 'flipboard' + | 'line' + | 'linkedin' + | 'messenger' + | 'pinterest' + | 'qq' + | 'qrcode' + | 'qzone' + | 'reddit' + | 'skype' + | 'sms' + | 'snapchat' + | 'telegram' + | 'tumblr' + | 'twitter' + | 'vk' + | 'weibo' + | 'whatsapp' + | 'wordpress' + +export type ShareServiceVariableName = + | 'cover' + | 'description' + | 'excerpt' + | 'image' + | 'summary' + | 'tags' + | 'title' + | 'twitterUserName' + | 'url' + +export interface ShareServiceConfig { + /** + * Share link + * + * @description You can use `[` and `]` to wrap the variable name, and the variable will be replaced with the value of the page.: + * + * - `title` will be replaced with the title of the page + * - `description` will be replaced with the description of the page + * - `url` will be replaced with the url of the page + * - `summary` will be replaced with the summary of the page + * - `tags` will be replaced with the tags of the page + * - `cover` will be replaced with the cover/banner of the page + * - `image` will be replaced with the first image of the page + * + * 分享链接 + * + * @description 你可以使用 `[` 和 `]` 包裹变量名,变量将会被替换为页面的值: + * + * - `title` 将会被替换为页面的标题 + * - `description` 将会被替换为页面的描述 + * - `url` 将会被替换为页面的链接 + * - `summary` 将会被替换为页面的综述 + * - `tags` 将会被替换为页面的标签 + * - `cover` 将会被替换为页面的封面 + * - `image` 将会被替换为页面的第一张图片 + */ + link: string + + /** + * Action of share button + * + * @description + * - `open` will open the link in a new tab + * - `navigate` will navigate to the link + * - `popup` will open a popup window + * - `qrcode` will show a QR code with link + * + * 分享按钮的行为 + * + * @description + * - `open` 将会在新标签页打开链接 + * - `navigate` 将会跳转到链接 + * - `popup` 将会打开一个弹窗 + * - `qrcode` 将会显示一个二维码 + * + * @default "popup" + */ + action?: ShareAction + + /** + * Theme color of icon + * + * 图标的主题色 + * + * @default 'currentColor' + */ + color?: string + + /** + * Plain icon shape + * + * 纯色图标的形状 + */ + shape: string + + /** + * Colorful icon + * + * 彩色图标 + */ + icon?: string +} + +export interface ShareServiceOptions extends ShareServiceConfig { + /** + * Service name + * + * 服务名称 + */ + name: string +} + +export type ShareService = BuiltInShareService | ShareServiceOptions diff --git a/plugins/features/plugin-media/src/shims-mse.d.ts b/plugins/features/plugin-media/src/shims-mse.d.ts new file mode 100644 index 0000000000..4984bde44c --- /dev/null +++ b/plugins/features/plugin-media/src/shims-mse.d.ts @@ -0,0 +1,11 @@ +declare module 'hls.js/dist/hls.min.js' { + import HLS from 'hls.js' + + export = HLS +} + +declare module 'mpegts.js/dist/mpegts.js' { + import mpegts from 'mpegts.js' + + export = mpegts +} diff --git a/plugins/features/plugin-media/tsconfig.build.json b/plugins/features/plugin-media/tsconfig.build.json new file mode 100644 index 0000000000..f7f7fe795a --- /dev/null +++ b/plugins/features/plugin-media/tsconfig.build.json @@ -0,0 +1,10 @@ +{ + "extends": "../../../tsconfig.build.json", + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib", + "types": ["vuepress/client-types", "vite/client", "webpack-env"] + }, + "include": ["./src"], + "references": [{ "path": "../../../tools/helper/tsconfig.build.json" }] +} diff --git a/plugins/features/plugin-medium-zoom/src/node/mediumZoomPlugin.ts b/plugins/features/plugin-medium-zoom/src/node/mediumZoomPlugin.ts index 2034f98d57..9155276a8c 100644 --- a/plugins/features/plugin-medium-zoom/src/node/mediumZoomPlugin.ts +++ b/plugins/features/plugin-medium-zoom/src/node/mediumZoomPlugin.ts @@ -1,6 +1,6 @@ import type { Plugin } from 'vuepress/core' import { getDirname, path } from 'vuepress/utils' -import type { MediumZoomPluginOptions } from './options' +import type { MediumZoomPluginOptions } from './options.js' const __dirname = import.meta.dirname || getDirname(import.meta.url) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index db28365f0a..27b58c2b1a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -679,6 +679,36 @@ importers: specifier: 'catalog:' version: 2.0.0-rc.26(@vuepress/bundler-vite@2.0.0-rc.26(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1))(@vuepress/bundler-webpack@2.0.0-rc.26(esbuild@0.25.11)(typescript@5.9.3))(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3)) + plugins/features/plugin-media: + dependencies: + '@vuepress/helper': + specifier: workspace:* + version: link:../../../tools/helper + '@vueuse/core': + specifier: 'catalog:' + version: 14.0.0(vue@3.5.22(typescript@5.9.3)) + artplayer: + specifier: ^5.3.0 + version: 5.3.0 + dashjs: + specifier: 4.7.4 + version: 4.7.4 + hls.js: + specifier: ^1.6.13 + version: 1.6.13 + mpegts.js: + specifier: ^1.7.3 + version: 1.8.0 + vidstack: + specifier: ^1.12.13 + version: 1.12.13 + vue: + specifier: 'catalog:' + version: 3.5.22(typescript@5.9.3) + vuepress: + specifier: 'catalog:' + version: 2.0.0-rc.26(@vuepress/bundler-vite@2.0.0-rc.26(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1))(@vuepress/bundler-webpack@2.0.0-rc.26(esbuild@0.25.11)(typescript@5.9.3))(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3)) + plugins/features/plugin-medium-zoom: dependencies: '@vuepress/helper': @@ -2600,6 +2630,9 @@ packages: '@floating-ui/dom@1.1.1': resolution: {integrity: sha512-TpIO93+DIujg3g7SykEAGZMDtbJRrmnYRCNYSjJlvIbGhBjRSNTLVbNeDQBrzy9qDgUbiWdc7KA0uZHZ2tJmiw==} + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} + '@floating-ui/utils@0.2.10': resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} @@ -4503,6 +4536,9 @@ packages: peerDependencies: marked: ^14.1.0 + artplayer@5.3.0: + resolution: {integrity: sha512-yExO39MpEg4P+bxgChxx1eJfiUPE4q1QQRLCmqGhlsj+ANuaoEkR8hF93LdI5ZyrAcIbJkuEndxEiUoKobifDw==} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -4571,6 +4607,15 @@ packages: batch@0.6.1: resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} + bcp-47-match@1.0.3: + resolution: {integrity: sha512-LggQ4YTdjWQSKELZF5JwchnBa1u0pIQSZf5lSdOHEdbVP55h0qICA/FUp3+W99q0xqxYa1ZQizTUH87gecII5w==} + + bcp-47-normalize@1.1.1: + resolution: {integrity: sha512-jWZ1Jdu3cs0EZdfCkS0UE9Gg01PtxnChjEBySeB+Zo6nkqtFfnvtoQQgP1qU1Oo4qgJgxhTI6Sf9y/pZIhPs0A==} + + bcp-47@1.0.8: + resolution: {integrity: sha512-Y9y1QNBBtYtv7hcmoX0tR+tUNSFZGZ6OL6vKPObq8BbOhkCoyayF6ogfLTgAli/KuAEbsYHYUNq2AQuY6IuLag==} + before-after-hook@4.0.0: resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} @@ -4795,6 +4840,9 @@ packages: resolution: {integrity: sha512-rtpaCbr164TPPh+zFdkWpCyZuKkjpAzODfaZCf/SVJZzJN+4bHQb/LP3Jzq5/+84um3XXY8r548XiWKSborwVw==} engines: {node: ^18.17.0 || >=20.5.0} + codem-isoboxer@0.3.9: + resolution: {integrity: sha512-4XOTqEzBWrGOZaMd+sTED2hLpzfBbiQCf1W6OBGkIHqk1D8uwy8WFLazVbdQwfDpQ+vf39lqTGPa9IhWW0roTA==} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -5264,6 +5312,9 @@ packages: resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} engines: {node: '>=12'} + dashjs@4.7.4: + resolution: {integrity: sha512-+hldo25QPP3H/NOwqUrvt4uKdMse60/Gsz9AUAnoYfhga8qHWq4nWiojUosOiigbigkDTCAn9ORcvUaKCvmfCA==} + data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} @@ -5560,6 +5611,9 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} + es6-promise@4.2.8: + resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==} + esbuild-loader@4.4.0: resolution: {integrity: sha512-4J+hXTpTtEdzUNLoY8ReqDNJx2NoldfiljRCiKbeYUuZmVaiJeDqFgyAzz8uOopaekwRoCcqBFyEroGQLFVZ1g==} peerDependencies: @@ -5796,6 +5850,9 @@ packages: fast-content-type-parse@3.0.0: resolution: {integrity: sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==} + fast-deep-equal@2.0.1: + resolution: {integrity: sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -6188,6 +6245,9 @@ packages: resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} engines: {node: '>=12.0.0'} + hls.js@1.6.13: + resolution: {integrity: sha512-hNEzjZNHf5bFrUNvdS4/1RjIanuJ6szpWNfTaX5I6WfGynWXGT7K/YQLYtemSvFExzeMdgdE4SsyVLJbd5PcZA==} + hookable@5.5.3: resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} @@ -6213,6 +6273,9 @@ packages: resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} engines: {node: '>=12'} + html-entities@1.4.0: + resolution: {integrity: sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==} + html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} @@ -6345,6 +6408,9 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + immutable@5.1.4: resolution: {integrity: sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==} @@ -6360,6 +6426,9 @@ packages: import-meta-resolve@4.2.0: resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + imsc@1.1.5: + resolution: {integrity: sha512-V8je+CGkcvGhgl2C1GlhqFFiUOIEdwXbXLiu1Fcubvvbo+g9inauqT3l0pNYXGoLPBj3jxtZz9t+wCopMkwadQ==} + imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -6815,6 +6884,9 @@ packages: resolution: {integrity: sha512-tNcU3cLH7toloAzhOOrBDhjzgbxpyuYvkf+BPPnnJCdc5EIcdJ8JcT+SglvCQKKyZ6m9dVXtCVlJcA6csxKdEA==} engines: {node: ^20.17.0 || >=22.9.0} + lie@3.1.1: + resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==} + lightningcss-android-arm64@1.30.2: resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==} engines: {node: '>= 12.0.0'} @@ -6898,8 +6970,11 @@ packages: lit-element@4.2.1: resolution: {integrity: sha512-WGAWRGzirAgyphK2urmYOV72tlvnxw7YfyLDgQ+OZnM9vQQBQnumQ7jUJe6unEzwGU3ahFOjuz1iz1jjrpCPuw==} - lit-html@3.3.1: - resolution: {integrity: sha512-S9hbyDu/vs1qNrithiNyeyv64c9yqiW9l+DBgI18fL+MTvOtWoFR0FWiyq1TxaYef5wNlpEmzlXoBlZEO+WjoA==} + lit-html@2.8.0: + resolution: {integrity: sha512-o9t+MQM3P4y7M7yNzqAyjp7z+mQGa4NS4CxiyLqFPyFWyc4O+nodLrkrxSaCTrla6M5YOLaT3RpbbqjszB5g3Q==} + + lit-html@3.3.0: + resolution: {integrity: sha512-RHoswrFAxY2d8Cf2mm4OZ1DgzCoBKUKSPvA1fhtSELxUERq2aQQ2h05pO9j81gS1o7RIRJ+CePLogfyahwmynw==} lit@3.3.1: resolution: {integrity: sha512-Ksr/8L3PTapbdXJCk+EJVB78jDodUMaP54gD24W186zGRARvwrsPfS60wae/SSCTCNZVPd1chXqio1qHQmu4NA==} @@ -6920,6 +6995,9 @@ packages: resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==} engines: {node: '>=14'} + localforage@1.10.0: + resolution: {integrity: sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==} + locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -7143,6 +7221,10 @@ packages: mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + media-captions@1.0.4: + resolution: {integrity: sha512-cyDNmuZvvO4H27rcBq2Eudxo9IZRDCOX/I7VEyqbxsEiD2Ei7UYUhG/Sc5fvMZjmathgz3fEK7iAKqvpY+Ux1w==} + engines: {node: '>=16'} + media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} @@ -7357,6 +7439,9 @@ packages: mlly@1.8.0: resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} + mpegts.js@1.8.0: + resolution: {integrity: sha512-ZtujqtmTjWgcDDkoOnLvrOKUTO/MKgLHM432zGDI8oPaJ0S+ebPxg1nEpDpLw6I7KmV/GZgUIrfbWi3qqEircg==} + ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -7545,6 +7630,9 @@ packages: resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} hasBin: true + option-validator@2.0.6: + resolution: {integrity: sha512-tmZDan2LRIRQyhUGvkff68/O0R8UmF+Btmiiz0SmSw2ng3CfPZB9wJlIjHpe/MKUZqyIZkVIXCrwr1tIN+0Dzg==} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -8504,6 +8592,9 @@ packages: engines: {node: '>=14.0.0'} hasBin: true + sax@1.2.1: + resolution: {integrity: sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==} + sax@1.4.1: resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} @@ -9166,6 +9257,10 @@ packages: engines: {node: '>=14.17'} hasBin: true + ua-parser-js@1.0.41: + resolution: {integrity: sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==} + hasBin: true + uc.micro@2.1.0: resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} @@ -9274,6 +9369,10 @@ packages: resolution: {integrity: sha512-gwXJnPRewT4rT7sBi/IvxKTjsms7jX7QIDLOClApuZwR49SXbrB1z2NLUZ+vDHyqCj/n58OzRRqaW+B8OZi8vg==} engines: {node: '>=18.12.0'} + unplugin@1.16.1: + resolution: {integrity: sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==} + engines: {node: '>=14.0.0'} + unplugin@2.3.10: resolution: {integrity: sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==} engines: {node: '>=18.12.0'} @@ -9351,6 +9450,10 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + vidstack@1.12.13: + resolution: {integrity: sha512-vuNeyRmWoH/7EoFVDYjp9nkgcqtCMmal518LDeb78dYKgWb+p6+vtY0AzDhrkBv5q1UiCn+xwmjMmwvSlPLuhQ==} + engines: {node: '>=18'} + vite@7.1.12: resolution: {integrity: sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==} engines: {node: ^20.19.0 || >=22.12.0} @@ -9576,6 +9679,10 @@ packages: resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} engines: {node: '>=0.8.0'} + webworkify-webpack@git+https://git@github.com:xqq/webworkify-webpack.git#24d1e719b4a6cac37a518b2bb10fe124527ef4ef: + resolution: {commit: 24d1e719b4a6cac37a518b2bb10fe124527ef4ef, repo: git@github.com:xqq/webworkify-webpack.git, type: git} + version: 2.1.5 + whatwg-encoding@2.0.0: resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} engines: {node: '>=12'} @@ -10973,6 +11080,11 @@ snapshots: dependencies: '@floating-ui/core': 1.7.3 + '@floating-ui/dom@1.7.4': + dependencies: + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 + '@floating-ui/utils@0.2.10': {} '@gera2ld/jsx-dom@2.2.2': @@ -13272,6 +13384,10 @@ snapshots: dependencies: marked: 16.4.1 + artplayer@5.3.0: + dependencies: + option-validator: 2.0.6 + assertion-error@2.0.1: {} astral-regex@2.0.0: {} @@ -13336,6 +13452,19 @@ snapshots: batch@0.6.1: {} + bcp-47-match@1.0.3: {} + + bcp-47-normalize@1.1.1: + dependencies: + bcp-47: 1.0.8 + bcp-47-match: 1.0.3 + + bcp-47@1.0.8: + dependencies: + is-alphabetical: 1.0.4 + is-alphanumerical: 1.0.4 + is-decimal: 1.0.4 + before-after-hook@4.0.0: {} big.js@5.2.2: {} @@ -13620,6 +13749,8 @@ snapshots: cmd-shim@7.0.0: {} + codem-isoboxer@0.3.9: {} + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -14136,6 +14267,19 @@ snapshots: dargs@8.1.0: {} + dashjs@4.7.4: + dependencies: + bcp-47-match: 1.0.3 + bcp-47-normalize: 1.1.1 + codem-isoboxer: 0.3.9 + es6-promise: 4.2.8 + fast-deep-equal: 2.0.1 + html-entities: 1.4.0 + imsc: 1.1.5 + localforage: 1.10.0 + path-browserify: 1.0.1 + ua-parser-js: 1.0.41 + data-view-buffer@1.0.2: dependencies: call-bound: 1.0.4 @@ -14448,6 +14592,8 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 + es6-promise@4.2.8: {} + esbuild-loader@4.4.0(webpack@5.102.1(esbuild@0.25.11)): dependencies: esbuild: 0.25.11 @@ -14776,6 +14922,8 @@ snapshots: fast-content-type-parse@3.0.0: {} + fast-deep-equal@2.0.1: {} + fast-deep-equal@3.1.3: {} fast-glob@3.3.3: @@ -15211,6 +15359,8 @@ snapshots: highlight.js@11.11.1: {} + hls.js@1.6.13: {} + hookable@5.5.3: {} hookified@1.12.2: {} @@ -15238,6 +15388,8 @@ snapshots: dependencies: whatwg-encoding: 2.0.0 + html-entities@1.4.0: {} + html-escaper@2.0.2: {} html-minifier-terser@6.1.0: @@ -15400,6 +15552,8 @@ snapshots: ignore@7.0.5: {} + immediate@3.0.6: {} + immutable@5.1.4: {} import-fresh@3.3.1: @@ -15414,6 +15568,10 @@ snapshots: import-meta-resolve@4.2.0: {} + imsc@1.1.5: + dependencies: + sax: 1.2.1 + imurmurhash@0.1.4: {} index-to-position@1.2.0: {} @@ -15835,6 +15993,10 @@ snapshots: transitivePeerDependencies: - supports-color + lie@3.1.1: + dependencies: + immediate: 3.0.6 + lightningcss-android-arm64@1.30.2: optional: true @@ -15896,9 +16058,13 @@ snapshots: dependencies: '@lit-labs/ssr-dom-shim': 1.4.0 '@lit/reactive-element': 2.1.1 - lit-html: 3.3.1 + lit-html: 3.3.0 + + lit-html@2.8.0: + dependencies: + '@types/trusted-types': 2.0.7 - lit-html@3.3.1: + lit-html@3.3.0: dependencies: '@types/trusted-types': 2.0.7 @@ -15906,7 +16072,7 @@ snapshots: dependencies: '@lit/reactive-element': 2.1.1 lit-element: 4.2.1 - lit-html: 3.3.1 + lit-html: 3.3.0 load-json-file@7.0.1: {} @@ -15924,6 +16090,10 @@ snapshots: pkg-types: 2.3.0 quansync: 0.2.11 + localforage@1.10.0: + dependencies: + lie: 3.1.1 + locate-path@5.0.0: dependencies: p-locate: 4.1.0 @@ -16248,6 +16418,8 @@ snapshots: mdurl@2.0.0: {} + media-captions@1.0.4: {} + media-typer@0.3.0: {} medium-zoom@1.1.0: {} @@ -16545,6 +16717,11 @@ snapshots: pkg-types: 1.3.1 ufo: 1.6.1 + mpegts.js@1.8.0: + dependencies: + es6-promise: 4.2.8 + webworkify-webpack: git+https://git@github.com:xqq/webworkify-webpack.git#24d1e719b4a6cac37a518b2bb10fe124527ef4ef + ms@2.0.0: {} ms@2.1.3: {} @@ -16734,6 +16911,10 @@ snapshots: opener@1.5.2: {} + option-validator@2.0.6: + dependencies: + kind-of: 6.0.3 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -17695,6 +17876,8 @@ snapshots: optionalDependencies: '@parcel/watcher': 2.5.1 + sax@1.2.1: {} + sax@1.4.1: {} schema-utils@4.3.3: @@ -18475,6 +18658,8 @@ snapshots: typescript@5.9.3: {} + ua-parser-js@1.0.41: {} + uc.micro@2.1.0: {} ufo@1.6.1: {} @@ -18582,6 +18767,11 @@ snapshots: pathe: 2.0.3 picomatch: 4.0.3 + unplugin@1.16.1: + dependencies: + acorn: 8.15.0 + webpack-virtual-modules: 0.6.2 + unplugin@2.3.10: dependencies: '@jridgewell/remapping': 2.3.5 @@ -18671,6 +18861,13 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 + vidstack@1.12.13: + dependencies: + '@floating-ui/dom': 1.7.4 + lit-html: 2.8.0 + media-captions: 1.0.4 + unplugin: 1.16.1 + vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1): dependencies: esbuild: 0.25.11 @@ -18932,6 +19129,8 @@ snapshots: websocket-extensions@0.1.4: {} + webworkify-webpack@git+https://git@github.com:xqq/webworkify-webpack.git#24d1e719b4a6cac37a518b2bb10fe124527ef4ef: {} + whatwg-encoding@2.0.0: dependencies: iconv-lite: 0.6.3 diff --git a/tsconfig.build.json b/tsconfig.build.json index 04148c01aa..9125d6fcb9 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -80,6 +80,7 @@ { "path": "./plugins/features/plugin-copy-code/tsconfig.build.json" }, { "path": "./plugins/features/plugin-copyright/tsconfig.build.json" }, { "path": "./plugins/features/plugin-icon/tsconfig.build.json" }, + { "path": "./plugins/features/plugin-media/tsconfig.build.json" }, { "path": "./plugins/features/plugin-medium-zoom/tsconfig.build.json" }, { "path": "./plugins/features/plugin-notice/tsconfig.build.json" }, { "path": "./plugins/features/plugin-nprogress/tsconfig.build.json" },