videojs-vue 0.0.0 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +183 -4
- package/dist/AlertDialog-BPntemoC.js +112 -0
- package/dist/Audio-3HgRIaNt.js +29 -0
- package/dist/Audio-rV5KrK6q.d.ts +7 -0
- package/dist/BufferingIndicator-Crzitkyo.js +64 -0
- package/dist/CaptionsButton-DZvnZsRf.js +124 -0
- package/dist/CastButton-B645yeE0.js +32 -0
- package/dist/Controls-CEaWmHRW.js +33 -0
- package/dist/ErrorDialog-Csv288q6.js +60 -0
- package/dist/FullscreenButton-C29YwATz.js +59 -0
- package/dist/Gesture-DFE17PKp.js +75 -0
- package/dist/Hotkey-DbQF7Kcc.js +72 -0
- package/dist/MuteButton-DI9PgX0j.js +47 -0
- package/dist/PiPButton-Da-rW3zX.js +55 -0
- package/dist/PlayButton-D8BRNiiy.js +51 -0
- package/dist/PlaybackRateButton-BL6cly9Z.js +60 -0
- package/dist/Popover-BoMi7OiJ.js +94 -0
- package/dist/Poster-uAFVQvx8.js +33 -0
- package/dist/SeekButton-VPcC9jiz.js +48 -0
- package/dist/Slider-C8FCipIJ.js +127 -0
- package/dist/Thumbnail-D1iL13r4.js +48 -0
- package/dist/Time-pfGZovLG.js +77 -0
- package/dist/TimeSlider-dlcFBLwL.js +147 -0
- package/dist/Tooltip-DyEkP_xv.js +119 -0
- package/dist/Video-DZX6iEK6.d.ts +7 -0
- package/dist/Video-DtjXh6lt.js +29 -0
- package/dist/VolumeSlider-C_nTFP4G.js +148 -0
- package/dist/alert-dialog.d.ts +2 -0
- package/dist/alert-dialog.js +2 -0
- package/dist/audio.d.ts +3 -0
- package/dist/audio.js +3 -0
- package/dist/background.d.ts +8 -0
- package/dist/background.js +19 -0
- package/dist/buffering-indicator.d.ts +2 -0
- package/dist/buffering-indicator.js +2 -0
- package/dist/captions-button.d.ts +2 -0
- package/dist/captions-button.js +2 -0
- package/dist/cast-button.d.ts +2 -0
- package/dist/cast-button.js +2 -0
- package/dist/context-C48Uabdu.js +83 -0
- package/dist/controls.d.ts +2 -0
- package/dist/controls.js +2 -0
- package/dist/createParts-Cq1bDfQi.d.ts +20 -0
- package/dist/createParts-DQhQPfRO.js +115 -0
- package/dist/error-dialog.d.ts +2 -0
- package/dist/error-dialog.js +2 -0
- package/dist/formatClock-D8z33znG.js +12 -0
- package/dist/full-screen-button.d.ts +2 -0
- package/dist/full-screen-button.js +2 -0
- package/dist/gesture.d.ts +2 -0
- package/dist/gesture.js +2 -0
- package/dist/hotkey.d.ts +2 -0
- package/dist/hotkey.js +2 -0
- package/dist/index-B3CYxKTC.d.ts +12 -0
- package/dist/index-B6WjYRnz.d.ts +33 -0
- package/dist/index-BJwWXroQ.d.ts +33 -0
- package/dist/index-BQN2NnER.d.ts +127 -0
- package/dist/index-BfzCw_wH.d.ts +71 -0
- package/dist/index-Bt_p2vCi.d.ts +176 -0
- package/dist/index-Bx7oSSQd.d.ts +33 -0
- package/dist/index-C8DkRbnH.d.ts +108 -0
- package/dist/index-CSs1G8z6.d.ts +7 -0
- package/dist/index-CcKZD20a.d.ts +90 -0
- package/dist/index-Cp23VNuH.d.ts +23 -0
- package/dist/index-Cry-CeV9.d.ts +57 -0
- package/dist/index-D5lfnRsK.d.ts +20 -0
- package/dist/index-D8EBYIXG.d.ts +38 -0
- package/dist/index-DYxPIvnt.d.ts +119 -0
- package/dist/index-DrelWSL1.d.ts +50 -0
- package/dist/index-EsA6hZWc.d.ts +23 -0
- package/dist/index-RI8VZ21i.d.ts +69 -0
- package/dist/index-iTm4WaZf.d.ts +33 -0
- package/dist/index-lmsQv-dG.d.ts +33 -0
- package/dist/index-w4PLuacs.d.ts +33 -0
- package/dist/index-ygLHhQAa.d.ts +7 -0
- package/dist/index.d.ts +129 -3
- package/dist/index.js +102 -3
- package/dist/live-audio.d.ts +6 -0
- package/dist/live-audio.js +6 -0
- package/dist/live-video.d.ts +6 -0
- package/dist/live-video.js +6 -0
- package/dist/mediaActions-Bhs90znv.js +39 -0
- package/dist/mute-button.d.ts +2 -0
- package/dist/mute-button.js +2 -0
- package/dist/pip-button.d.ts +2 -0
- package/dist/pip-button.js +2 -0
- package/dist/play-button.d.ts +2 -0
- package/dist/play-button.js +2 -0
- package/dist/playback-rate-button.d.ts +2 -0
- package/dist/playback-rate-button.js +2 -0
- package/dist/popover.d.ts +2 -0
- package/dist/popover.js +2 -0
- package/dist/poster.d.ts +2 -0
- package/dist/poster.js +2 -0
- package/dist/renderElement-D_UbvLk5.js +114 -0
- package/dist/seek-button.d.ts +2 -0
- package/dist/seek-button.js +2 -0
- package/dist/slider.d.ts +3 -0
- package/dist/slider.js +2 -0
- package/dist/thumbnail.d.ts +2 -0
- package/dist/thumbnail.js +2 -0
- package/dist/time-slider.d.ts +3 -0
- package/dist/time-slider.js +2 -0
- package/dist/time.d.ts +2 -0
- package/dist/time.js +2 -0
- package/dist/tooltip.d.ts +2 -0
- package/dist/tooltip.js +2 -0
- package/dist/useMediaEvents-Br7vzJ_l.js +17 -0
- package/dist/useSyncMediaProps-C7F52G6b.js +95 -0
- package/dist/video.d.ts +3 -0
- package/dist/video.js +3 -0
- package/dist/volume-slider.d.ts +3 -0
- package/dist/volume-slider.js +2 -0
- package/package.json +132 -16
package/README.md
CHANGED
|
@@ -5,13 +5,17 @@
|
|
|
5
5
|
[](https://www.npmjs.com/package/videojs-vue)
|
|
6
6
|
[](https://github.com/ntnyq/videojs-vue/blob/main/LICENSE)
|
|
7
7
|
|
|
8
|
-
A
|
|
8
|
+
A Vue-first wrapper around the Video.js v10 player primitives.
|
|
9
9
|
|
|
10
10
|
> [!NOTE]
|
|
11
|
-
> Status: **
|
|
11
|
+
> Status: **Beta**
|
|
12
12
|
|
|
13
13
|
## Install
|
|
14
14
|
|
|
15
|
+
```shell
|
|
16
|
+
pnpm add videojs-vue
|
|
17
|
+
```
|
|
18
|
+
|
|
15
19
|
```shell
|
|
16
20
|
npm install videojs-vue
|
|
17
21
|
```
|
|
@@ -20,10 +24,185 @@ npm install videojs-vue
|
|
|
20
24
|
yarn add videojs-vue
|
|
21
25
|
```
|
|
22
26
|
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
The API follows the upstream `@videojs/react` player factory:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { createPlayer } from 'videojs-vue'
|
|
33
|
+
import { Video, videoFeatures } from 'videojs-vue/video'
|
|
34
|
+
|
|
35
|
+
const Player = createPlayer({
|
|
36
|
+
features: videoFeatures,
|
|
37
|
+
displayName: 'VideoPlayer',
|
|
38
|
+
})
|
|
25
39
|
```
|
|
26
40
|
|
|
41
|
+
Render it with Vue's `h()` API or JSX:
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import { defineComponent, h } from 'vue'
|
|
45
|
+
|
|
46
|
+
export default defineComponent({
|
|
47
|
+
setup() {
|
|
48
|
+
const paused = Player.usePlayer(state => state.paused)
|
|
49
|
+
const store = Player.usePlayer()
|
|
50
|
+
|
|
51
|
+
return () =>
|
|
52
|
+
h(Player.Provider, () =>
|
|
53
|
+
h(Player.Container, { class: 'player-shell' }, () => [
|
|
54
|
+
h(Video, {
|
|
55
|
+
src: 'https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4',
|
|
56
|
+
playsInline: true,
|
|
57
|
+
}),
|
|
58
|
+
h(
|
|
59
|
+
'button',
|
|
60
|
+
{
|
|
61
|
+
type: 'button',
|
|
62
|
+
onClick: () => (paused.value ? store.play() : store.pause()),
|
|
63
|
+
},
|
|
64
|
+
paused.value ? 'Play' : 'Pause',
|
|
65
|
+
),
|
|
66
|
+
]),
|
|
67
|
+
)
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## API
|
|
73
|
+
|
|
74
|
+
### Root exports
|
|
75
|
+
|
|
76
|
+
- `createPlayer(config)` returns:
|
|
77
|
+
- `Provider`
|
|
78
|
+
- `Container`
|
|
79
|
+
- `usePlayer()`
|
|
80
|
+
- `useMedia()`
|
|
81
|
+
- `createPlayer({ plugins })` supports runtime extension hooks:
|
|
82
|
+
- `onProviderSetup(context)`
|
|
83
|
+
- `onAttach(context)`
|
|
84
|
+
- `onDetach(context)`
|
|
85
|
+
- `onProviderDestroy(context)`
|
|
86
|
+
- `usePlayer(selector)` returns a readonly `ref`, which is the Vue equivalent of the React hook's reactive selected value.
|
|
87
|
+
- `useOptionalPlayer()`, `useContainer()`, `useMediaAttach()` and `useContainerAttach()` are also exported for lower-level integrations.
|
|
88
|
+
- Utility exports:
|
|
89
|
+
- `mergeProps(...props)`
|
|
90
|
+
- `renderElement(tag, componentProps, params)`
|
|
91
|
+
|
|
92
|
+
### Preset entry points
|
|
93
|
+
|
|
94
|
+
- `videojs-vue/video`
|
|
95
|
+
- `Video`
|
|
96
|
+
- `videoFeatures`
|
|
97
|
+
- `videojs-vue/audio`
|
|
98
|
+
- `Audio`
|
|
99
|
+
- `audioFeatures`
|
|
100
|
+
- `videojs-vue/background`
|
|
101
|
+
- `BackgroundVideo`
|
|
102
|
+
- `backgroundFeatures`
|
|
103
|
+
- `videojs-vue/live-video`
|
|
104
|
+
- `Video`
|
|
105
|
+
- `liveVideoFeatures`
|
|
106
|
+
- `videojs-vue/live-audio`
|
|
107
|
+
- `Audio`
|
|
108
|
+
- `liveAudioFeatures`
|
|
109
|
+
|
|
110
|
+
### UI entry points
|
|
111
|
+
|
|
112
|
+
- `videojs-vue/full-screen-button` (or `videojs-vue/fullscreen-button`)
|
|
113
|
+
- `FullscreenButton`
|
|
114
|
+
- `videojs-vue/playback-rate-button`
|
|
115
|
+
- `PlaybackRateButton`
|
|
116
|
+
- `videojs-vue/poster`
|
|
117
|
+
- `Poster`
|
|
118
|
+
- `videojs-vue/time`
|
|
119
|
+
- `Time.Group`
|
|
120
|
+
- `Time.Separator`
|
|
121
|
+
- `Time.Value`
|
|
122
|
+
- `videojs-vue/play-button`
|
|
123
|
+
- `PlayButton`
|
|
124
|
+
- `videojs-vue/mute-button`
|
|
125
|
+
- `MuteButton`
|
|
126
|
+
- `videojs-vue/pip-button`
|
|
127
|
+
- `PiPButton`
|
|
128
|
+
- `videojs-vue/seek-button`
|
|
129
|
+
- `SeekButton`
|
|
130
|
+
- `videojs-vue/volume-slider`
|
|
131
|
+
- `VolumeSlider.Root`
|
|
132
|
+
- `VolumeSlider.Track`
|
|
133
|
+
- `VolumeSlider.Fill`
|
|
134
|
+
- `VolumeSlider.Thumb`
|
|
135
|
+
- `VolumeSlider.Value`
|
|
136
|
+
- `VolumeSlider.Preview`
|
|
137
|
+
- `videojs-vue/time-slider`
|
|
138
|
+
- `TimeSlider.Root`
|
|
139
|
+
- `TimeSlider.Track`
|
|
140
|
+
- `TimeSlider.Buffer`
|
|
141
|
+
- `TimeSlider.Fill`
|
|
142
|
+
- `TimeSlider.Thumb`
|
|
143
|
+
- `TimeSlider.Value`
|
|
144
|
+
- `TimeSlider.Preview`
|
|
145
|
+
- `videojs-vue/controls`
|
|
146
|
+
- `Controls.Root`
|
|
147
|
+
- `Controls.Group`
|
|
148
|
+
- `videojs-vue/tooltip`
|
|
149
|
+
- `Tooltip.Provider`
|
|
150
|
+
- `Tooltip.Root`
|
|
151
|
+
- `Tooltip.Trigger`
|
|
152
|
+
- `Tooltip.Popup`
|
|
153
|
+
- `Tooltip.Arrow`
|
|
154
|
+
- `videojs-vue/popover`
|
|
155
|
+
- `Popover.Root`
|
|
156
|
+
- `Popover.Trigger`
|
|
157
|
+
- `Popover.Popup`
|
|
158
|
+
- `Popover.Arrow`
|
|
159
|
+
- `videojs-vue/captions-button`
|
|
160
|
+
- `CaptionsButton`
|
|
161
|
+
- `videojs-vue/cast-button`
|
|
162
|
+
- `CastButton`
|
|
163
|
+
- `videojs-vue/buffering-indicator`
|
|
164
|
+
- `BufferingIndicator`
|
|
165
|
+
- `videojs-vue/alert-dialog`
|
|
166
|
+
- `AlertDialog.Root`
|
|
167
|
+
- `AlertDialog.Popup`
|
|
168
|
+
- `AlertDialog.Title`
|
|
169
|
+
- `AlertDialog.Description`
|
|
170
|
+
- `AlertDialog.Close`
|
|
171
|
+
- `videojs-vue/error-dialog`
|
|
172
|
+
- `ErrorDialog.Root`
|
|
173
|
+
- `ErrorDialog.Popup`
|
|
174
|
+
- `ErrorDialog.Title`
|
|
175
|
+
- `ErrorDialog.Description`
|
|
176
|
+
- `ErrorDialog.Close`
|
|
177
|
+
- `videojs-vue/hotkey`
|
|
178
|
+
- `Hotkey`
|
|
179
|
+
- `videojs-vue/gesture`
|
|
180
|
+
- `Gesture`
|
|
181
|
+
- `videojs-vue/thumbnail`
|
|
182
|
+
- `Thumbnail`
|
|
183
|
+
- `videojs-vue/slider`
|
|
184
|
+
- `Slider.Root`
|
|
185
|
+
- `Slider.Track`
|
|
186
|
+
- `Slider.Buffer`
|
|
187
|
+
- `Slider.Fill`
|
|
188
|
+
- `Slider.Thumb`
|
|
189
|
+
- `Slider.Value`
|
|
190
|
+
- `Slider.Preview`
|
|
191
|
+
- `Slider.Thumbnail`
|
|
192
|
+
|
|
193
|
+
## Notes
|
|
194
|
+
|
|
195
|
+
- `Provider` owns the Video.js store instance and reattaches it whenever the media element or container changes.
|
|
196
|
+
- `Container` keeps the same focus behavior as the React implementation: on pointer release it focuses itself when nothing inside already has focus, so keyboard shortcuts can work reliably.
|
|
197
|
+
- Media components use native `<video>` / `<audio>` elements, register themselves automatically through provide/inject, and reactively sync key media props (`src`, `poster`, `autoplay`, `controls`, `loop`, `muted`, `playsInline`, `preload`, `crossOrigin`, `volume`, `playbackRate`).
|
|
198
|
+
- Captions button now uses reactive track state and auto-disables when no caption/subtitle tracks are available.
|
|
199
|
+
|
|
200
|
+
## Migration (Current Beta)
|
|
201
|
+
|
|
202
|
+
- UI button primitives now use unified internal prop-merging behavior (`mergeProps`) instead of per-component manual click chaining.
|
|
203
|
+
- External event handlers still run before internal handlers and can cancel internal behavior via `event.preventDefault()`.
|
|
204
|
+
- Prefer `playsInline` over lowercase `playsinline` in JSX/templates.
|
|
205
|
+
|
|
27
206
|
## License
|
|
28
207
|
|
|
29
208
|
[MIT](./LICENSE) License © 2026-PRESENT [ntnyq](https://github.com/ntnyq)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { t as renderElement } from "./renderElement-D_UbvLk5.js";
|
|
2
|
+
import { computed, defineComponent, h, inject, provide, shallowRef, watch } from "vue";
|
|
3
|
+
//#region src/ui/alert-dialog/AlertDialog.ts
|
|
4
|
+
const ALERT_DIALOG_KEY = Symbol("videojs-alert-dialog");
|
|
5
|
+
function useAlertDialogContext() {
|
|
6
|
+
const context = inject(ALERT_DIALOG_KEY, null);
|
|
7
|
+
if (!context) throw new Error("AlertDialog parts must be used within AlertDialog.Root");
|
|
8
|
+
return context;
|
|
9
|
+
}
|
|
10
|
+
const AlertDialog = {
|
|
11
|
+
Root: defineComponent({
|
|
12
|
+
name: "VideoJsAlertDialogRoot",
|
|
13
|
+
inheritAttrs: false,
|
|
14
|
+
props: {
|
|
15
|
+
open: {
|
|
16
|
+
type: Boolean,
|
|
17
|
+
default: void 0
|
|
18
|
+
},
|
|
19
|
+
defaultOpen: {
|
|
20
|
+
type: Boolean,
|
|
21
|
+
default: false
|
|
22
|
+
},
|
|
23
|
+
onOpenChange: {
|
|
24
|
+
type: Function,
|
|
25
|
+
default: void 0
|
|
26
|
+
},
|
|
27
|
+
onOpenChangeComplete: {
|
|
28
|
+
type: Function,
|
|
29
|
+
default: void 0
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
setup(props, { attrs, slots }) {
|
|
33
|
+
const internalOpen = shallowRef(Boolean(props.defaultOpen));
|
|
34
|
+
const controlled = computed(() => props.open !== void 0);
|
|
35
|
+
const open = computed(() => controlled.value ? Boolean(props.open) : internalOpen.value);
|
|
36
|
+
function setOpen(nextOpen) {
|
|
37
|
+
if (!controlled.value) internalOpen.value = nextOpen;
|
|
38
|
+
props.onOpenChange?.(nextOpen);
|
|
39
|
+
props.onOpenChangeComplete?.(nextOpen);
|
|
40
|
+
}
|
|
41
|
+
watch(() => props.open, (value) => {
|
|
42
|
+
if (controlled.value && typeof value === "boolean") internalOpen.value = value;
|
|
43
|
+
}, { immediate: true });
|
|
44
|
+
provide(ALERT_DIALOG_KEY, {
|
|
45
|
+
open,
|
|
46
|
+
setOpen
|
|
47
|
+
});
|
|
48
|
+
return () => h("div", { ...attrs }, slots["default"]?.());
|
|
49
|
+
}
|
|
50
|
+
}),
|
|
51
|
+
Popup: defineComponent({
|
|
52
|
+
name: "VideoJsAlertDialogPopup",
|
|
53
|
+
inheritAttrs: false,
|
|
54
|
+
setup(_, { attrs, slots, expose }) {
|
|
55
|
+
const element = shallowRef(null);
|
|
56
|
+
const dialog = useAlertDialogContext();
|
|
57
|
+
expose({ el: element });
|
|
58
|
+
return () => {
|
|
59
|
+
if (!dialog.open.value) return null;
|
|
60
|
+
return h("div", {
|
|
61
|
+
...attrs,
|
|
62
|
+
ref: ((value) => {
|
|
63
|
+
element.value = value;
|
|
64
|
+
}),
|
|
65
|
+
role: "alertdialog",
|
|
66
|
+
"aria-modal": "true"
|
|
67
|
+
}, slots["default"]?.());
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}),
|
|
71
|
+
Title: defineComponent({
|
|
72
|
+
name: "VideoJsAlertDialogTitle",
|
|
73
|
+
inheritAttrs: false,
|
|
74
|
+
setup(_, { attrs, slots }) {
|
|
75
|
+
return () => h("h2", { ...attrs }, slots["default"]?.());
|
|
76
|
+
}
|
|
77
|
+
}),
|
|
78
|
+
Description: defineComponent({
|
|
79
|
+
name: "VideoJsAlertDialogDescription",
|
|
80
|
+
inheritAttrs: false,
|
|
81
|
+
setup(_, { attrs, slots }) {
|
|
82
|
+
return () => h("p", { ...attrs }, slots["default"]?.());
|
|
83
|
+
}
|
|
84
|
+
}),
|
|
85
|
+
Close: defineComponent({
|
|
86
|
+
name: "VideoJsAlertDialogClose",
|
|
87
|
+
inheritAttrs: false,
|
|
88
|
+
setup(_, { attrs, slots, expose }) {
|
|
89
|
+
const element = shallowRef(null);
|
|
90
|
+
const dialog = useAlertDialogContext();
|
|
91
|
+
expose({ el: element });
|
|
92
|
+
return () => {
|
|
93
|
+
const content = slots["default"]?.() ?? "Close";
|
|
94
|
+
return renderElement("button", {}, {
|
|
95
|
+
state: { open: dialog.open.value },
|
|
96
|
+
ref: (value) => {
|
|
97
|
+
element.value = value;
|
|
98
|
+
},
|
|
99
|
+
props: [{
|
|
100
|
+
type: "button",
|
|
101
|
+
children: content,
|
|
102
|
+
onClick: (event) => {
|
|
103
|
+
if (!event.defaultPrevented) dialog.setOpen(false);
|
|
104
|
+
}
|
|
105
|
+
}, attrs]
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
};
|
|
111
|
+
//#endregion
|
|
112
|
+
export { useAlertDialogContext as n, AlertDialog as t };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { o as useMediaAttach } from "./context-C48Uabdu.js";
|
|
2
|
+
import { t as useSyncMediaProps } from "./useSyncMediaProps-C7F52G6b.js";
|
|
3
|
+
import { defineComponent, h, onBeforeUnmount, shallowRef, watch } from "vue";
|
|
4
|
+
//#region src/media/Audio.ts
|
|
5
|
+
const Audio = defineComponent({
|
|
6
|
+
name: "VideoJsAudio",
|
|
7
|
+
inheritAttrs: false,
|
|
8
|
+
setup(_, { attrs, expose, slots }) {
|
|
9
|
+
const setMedia = useMediaAttach();
|
|
10
|
+
const element = shallowRef(null);
|
|
11
|
+
watch(element, (value) => {
|
|
12
|
+
setMedia?.(value);
|
|
13
|
+
}, { immediate: true });
|
|
14
|
+
useSyncMediaProps(element, attrs);
|
|
15
|
+
onBeforeUnmount(() => {
|
|
16
|
+
const current = element.value;
|
|
17
|
+
setMedia?.((previous) => previous === current ? null : previous);
|
|
18
|
+
});
|
|
19
|
+
expose({ el: element });
|
|
20
|
+
return () => h("audio", {
|
|
21
|
+
...attrs,
|
|
22
|
+
ref: ((value) => {
|
|
23
|
+
element.value = value;
|
|
24
|
+
})
|
|
25
|
+
}, slots["default"]?.());
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
//#endregion
|
|
29
|
+
export { Audio as t };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { AudioHTMLAttributes } from "vue";
|
|
2
|
+
|
|
3
|
+
//#region src/media/Audio.d.ts
|
|
4
|
+
interface AudioProps extends AudioHTMLAttributes {}
|
|
5
|
+
declare const Audio: import("vue").DefineComponent<AudioProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<AudioProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { AudioProps as n, Audio as t };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { a as useMedia } from "./context-C48Uabdu.js";
|
|
2
|
+
import { t as useMediaEvents } from "./useMediaEvents-Br7vzJ_l.js";
|
|
3
|
+
import { defineComponent, h, onBeforeUnmount, shallowRef } from "vue";
|
|
4
|
+
//#region src/ui/buffering-indicator/BufferingIndicator.ts
|
|
5
|
+
const BufferingIndicator = defineComponent({
|
|
6
|
+
name: "VideoJsBufferingIndicator",
|
|
7
|
+
inheritAttrs: false,
|
|
8
|
+
props: { delay: {
|
|
9
|
+
type: Number,
|
|
10
|
+
default: 500
|
|
11
|
+
} },
|
|
12
|
+
setup(props, { attrs, slots }) {
|
|
13
|
+
const media = useMedia();
|
|
14
|
+
const visible = shallowRef(false);
|
|
15
|
+
let timer = null;
|
|
16
|
+
function clearTimer() {
|
|
17
|
+
if (!timer) return;
|
|
18
|
+
clearTimeout(timer);
|
|
19
|
+
timer = null;
|
|
20
|
+
}
|
|
21
|
+
function queueShow() {
|
|
22
|
+
clearTimer();
|
|
23
|
+
timer = setTimeout(() => {
|
|
24
|
+
visible.value = true;
|
|
25
|
+
}, Math.max(0, props.delay));
|
|
26
|
+
}
|
|
27
|
+
function hide() {
|
|
28
|
+
clearTimer();
|
|
29
|
+
visible.value = false;
|
|
30
|
+
}
|
|
31
|
+
useMediaEvents(media, {
|
|
32
|
+
onDetach: hide,
|
|
33
|
+
bindings: () => [
|
|
34
|
+
{
|
|
35
|
+
type: "waiting",
|
|
36
|
+
listener: queueShow
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
type: "stalled",
|
|
40
|
+
listener: queueShow
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
type: "playing",
|
|
44
|
+
listener: hide
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
type: "canplay",
|
|
48
|
+
listener: hide
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
});
|
|
52
|
+
onBeforeUnmount(() => {
|
|
53
|
+
hide();
|
|
54
|
+
});
|
|
55
|
+
return () => h("div", {
|
|
56
|
+
...attrs,
|
|
57
|
+
hidden: !visible.value,
|
|
58
|
+
"aria-hidden": !visible.value,
|
|
59
|
+
"aria-busy": visible.value
|
|
60
|
+
}, slots["default"]?.());
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
//#endregion
|
|
64
|
+
export { BufferingIndicator as t };
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { a as useMedia } from "./context-C48Uabdu.js";
|
|
2
|
+
import { t as renderElement } from "./renderElement-D_UbvLk5.js";
|
|
3
|
+
import { computed, defineComponent, shallowRef, watch } from "vue";
|
|
4
|
+
//#region src/ui/factories/useCaptionTracks.ts
|
|
5
|
+
function getCaptionTrackList(media) {
|
|
6
|
+
const synthetic = media?.__vjsTextTracks;
|
|
7
|
+
if (Array.isArray(synthetic)) return synthetic.filter((track) => track.kind === "captions" || track.kind === "subtitles");
|
|
8
|
+
const textTracks = media?.textTracks;
|
|
9
|
+
if (!textTracks) return [];
|
|
10
|
+
return [...textTracks].filter((track) => track.kind === "captions" || track.kind === "subtitles");
|
|
11
|
+
}
|
|
12
|
+
function useCaptionTracks(media) {
|
|
13
|
+
const tracks = shallowRef([]);
|
|
14
|
+
const enabled = computed(() => tracks.value.some((track) => track.mode === "showing"));
|
|
15
|
+
const selectedIndex = computed(() => tracks.value.findIndex((track) => track.mode === "showing"));
|
|
16
|
+
const refresh = () => {
|
|
17
|
+
tracks.value = getCaptionTrackList(media.value).map((track, index) => ({
|
|
18
|
+
index,
|
|
19
|
+
kind: track.kind,
|
|
20
|
+
label: track.label,
|
|
21
|
+
language: track.language,
|
|
22
|
+
mode: track.mode
|
|
23
|
+
}));
|
|
24
|
+
};
|
|
25
|
+
const hideAll = () => {
|
|
26
|
+
const list = getCaptionTrackList(media.value);
|
|
27
|
+
for (const track of list) track.mode = "hidden";
|
|
28
|
+
refresh();
|
|
29
|
+
};
|
|
30
|
+
const show = (index) => {
|
|
31
|
+
const list = getCaptionTrackList(media.value);
|
|
32
|
+
if (list.length === 0) return;
|
|
33
|
+
const nextIndex = Math.max(0, Math.min(index, list.length - 1));
|
|
34
|
+
for (const [trackIndex, track] of list.entries()) track.mode = trackIndex === nextIndex ? "showing" : "hidden";
|
|
35
|
+
refresh();
|
|
36
|
+
};
|
|
37
|
+
const toggle = () => {
|
|
38
|
+
if (enabled.value) {
|
|
39
|
+
hideAll();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const index = selectedIndex.value;
|
|
43
|
+
show(Math.max(index, 0));
|
|
44
|
+
};
|
|
45
|
+
watch(media, (node, _, onCleanup) => {
|
|
46
|
+
refresh();
|
|
47
|
+
if (!node) return;
|
|
48
|
+
const onChange = () => {
|
|
49
|
+
refresh();
|
|
50
|
+
};
|
|
51
|
+
node.addEventListener("loadedmetadata", onChange);
|
|
52
|
+
node.addEventListener("emptied", onChange);
|
|
53
|
+
const list = node.textTracks;
|
|
54
|
+
if (list?.addEventListener) {
|
|
55
|
+
list.addEventListener("change", onChange);
|
|
56
|
+
list.addEventListener("addtrack", onChange);
|
|
57
|
+
list.addEventListener("removetrack", onChange);
|
|
58
|
+
}
|
|
59
|
+
onCleanup(() => {
|
|
60
|
+
node.removeEventListener("loadedmetadata", onChange);
|
|
61
|
+
node.removeEventListener("emptied", onChange);
|
|
62
|
+
if (list?.removeEventListener) {
|
|
63
|
+
list.removeEventListener("change", onChange);
|
|
64
|
+
list.removeEventListener("addtrack", onChange);
|
|
65
|
+
list.removeEventListener("removetrack", onChange);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}, { immediate: true });
|
|
69
|
+
return {
|
|
70
|
+
tracks,
|
|
71
|
+
enabled,
|
|
72
|
+
selectedIndex,
|
|
73
|
+
refresh,
|
|
74
|
+
hideAll,
|
|
75
|
+
show,
|
|
76
|
+
toggle
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/ui/captions-button/CaptionsButton.ts
|
|
81
|
+
const CaptionsButton = defineComponent({
|
|
82
|
+
name: "VideoJsCaptionsButton",
|
|
83
|
+
inheritAttrs: false,
|
|
84
|
+
props: {
|
|
85
|
+
onLabel: {
|
|
86
|
+
type: String,
|
|
87
|
+
default: "Captions on"
|
|
88
|
+
},
|
|
89
|
+
offLabel: {
|
|
90
|
+
type: String,
|
|
91
|
+
default: "Captions off"
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
setup(props, { attrs, slots, expose }) {
|
|
95
|
+
const element = shallowRef(null);
|
|
96
|
+
const captionTracks = useCaptionTracks(useMedia());
|
|
97
|
+
const label = computed(() => captionTracks.enabled.value ? props.onLabel : props.offLabel);
|
|
98
|
+
expose({ el: element });
|
|
99
|
+
return () => {
|
|
100
|
+
const content = slots["default"]?.() ?? label.value;
|
|
101
|
+
return renderElement("button", {}, {
|
|
102
|
+
state: {
|
|
103
|
+
enabled: captionTracks.enabled.value,
|
|
104
|
+
trackCount: captionTracks.tracks.value.length
|
|
105
|
+
},
|
|
106
|
+
ref: (value) => {
|
|
107
|
+
element.value = value;
|
|
108
|
+
},
|
|
109
|
+
props: [{
|
|
110
|
+
type: "button",
|
|
111
|
+
"aria-label": label.value,
|
|
112
|
+
"aria-pressed": captionTracks.enabled.value,
|
|
113
|
+
children: content,
|
|
114
|
+
onClick: (event) => {
|
|
115
|
+
if (event.defaultPrevented) return;
|
|
116
|
+
captionTracks.toggle();
|
|
117
|
+
}
|
|
118
|
+
}, attrs]
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
//#endregion
|
|
124
|
+
export { CaptionsButton as t };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { a as useMedia } from "./context-C48Uabdu.js";
|
|
2
|
+
import { t as renderElement } from "./renderElement-D_UbvLk5.js";
|
|
3
|
+
import { defineComponent, shallowRef } from "vue";
|
|
4
|
+
//#region src/ui/cast-button/CastButton.ts
|
|
5
|
+
const CastButton = defineComponent({
|
|
6
|
+
name: "VideoJsCastButton",
|
|
7
|
+
inheritAttrs: false,
|
|
8
|
+
setup(_, { attrs, slots, expose }) {
|
|
9
|
+
const element = shallowRef(null);
|
|
10
|
+
const media = useMedia();
|
|
11
|
+
expose({ el: element });
|
|
12
|
+
return () => {
|
|
13
|
+
const content = slots["default"]?.() ?? "Cast";
|
|
14
|
+
return renderElement("button", {}, {
|
|
15
|
+
state: { hasMedia: Boolean(media.value) },
|
|
16
|
+
ref: (value) => {
|
|
17
|
+
element.value = value;
|
|
18
|
+
},
|
|
19
|
+
props: [{
|
|
20
|
+
type: "button",
|
|
21
|
+
children: content,
|
|
22
|
+
onClick: async (event) => {
|
|
23
|
+
if (event.defaultPrevented) return;
|
|
24
|
+
await media.value?.remote?.prompt?.();
|
|
25
|
+
}
|
|
26
|
+
}, attrs]
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
//#endregion
|
|
32
|
+
export { CastButton as t };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { defineComponent, h, shallowRef } from "vue";
|
|
2
|
+
const Controls = {
|
|
3
|
+
Root: defineComponent({
|
|
4
|
+
name: "VideoJsControlsRoot",
|
|
5
|
+
inheritAttrs: false,
|
|
6
|
+
setup(_, { attrs, slots, expose }) {
|
|
7
|
+
const element = shallowRef(null);
|
|
8
|
+
expose({ el: element });
|
|
9
|
+
return () => h("div", {
|
|
10
|
+
...attrs,
|
|
11
|
+
ref: ((value) => {
|
|
12
|
+
element.value = value;
|
|
13
|
+
})
|
|
14
|
+
}, slots["default"]?.());
|
|
15
|
+
}
|
|
16
|
+
}),
|
|
17
|
+
Group: defineComponent({
|
|
18
|
+
name: "VideoJsControlsGroup",
|
|
19
|
+
inheritAttrs: false,
|
|
20
|
+
setup(_, { attrs, slots, expose }) {
|
|
21
|
+
const element = shallowRef(null);
|
|
22
|
+
expose({ el: element });
|
|
23
|
+
return () => h("div", {
|
|
24
|
+
...attrs,
|
|
25
|
+
ref: ((value) => {
|
|
26
|
+
element.value = value;
|
|
27
|
+
})
|
|
28
|
+
}, slots["default"]?.());
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
};
|
|
32
|
+
//#endregion
|
|
33
|
+
export { Controls as t };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { a as useMedia } from "./context-C48Uabdu.js";
|
|
2
|
+
import { t as useMediaEvents } from "./useMediaEvents-Br7vzJ_l.js";
|
|
3
|
+
import { t as AlertDialog } from "./AlertDialog-BPntemoC.js";
|
|
4
|
+
import { computed, defineComponent, h, inject, provide, shallowRef } from "vue";
|
|
5
|
+
//#region src/ui/error-dialog/ErrorDialog.ts
|
|
6
|
+
const ERROR_DIALOG_KEY = Symbol("videojs-error-dialog");
|
|
7
|
+
function useErrorDialogContext() {
|
|
8
|
+
const context = inject(ERROR_DIALOG_KEY, null);
|
|
9
|
+
if (!context) throw new Error("ErrorDialog parts must be used within ErrorDialog.Root");
|
|
10
|
+
return context;
|
|
11
|
+
}
|
|
12
|
+
const Root = defineComponent({
|
|
13
|
+
name: "VideoJsErrorDialogRoot",
|
|
14
|
+
inheritAttrs: false,
|
|
15
|
+
setup(_, { attrs, slots }) {
|
|
16
|
+
const media = useMedia();
|
|
17
|
+
const lastError = shallowRef(null);
|
|
18
|
+
const sync = () => {
|
|
19
|
+
const node = media.value;
|
|
20
|
+
lastError.value = node ? node.error ?? null : null;
|
|
21
|
+
};
|
|
22
|
+
const clear = () => {
|
|
23
|
+
lastError.value = null;
|
|
24
|
+
};
|
|
25
|
+
useMediaEvents(media, {
|
|
26
|
+
onAttach: sync,
|
|
27
|
+
onDetach: clear,
|
|
28
|
+
bindings: () => [{
|
|
29
|
+
type: "error",
|
|
30
|
+
listener: sync
|
|
31
|
+
}, {
|
|
32
|
+
type: "loadeddata",
|
|
33
|
+
listener: clear
|
|
34
|
+
}]
|
|
35
|
+
});
|
|
36
|
+
const open = computed(() => Boolean(lastError.value));
|
|
37
|
+
provide(ERROR_DIALOG_KEY, { lastError });
|
|
38
|
+
return () => h(AlertDialog.Root, {
|
|
39
|
+
...attrs,
|
|
40
|
+
open: open.value
|
|
41
|
+
}, slots);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
const Description = defineComponent({
|
|
45
|
+
name: "VideoJsErrorDialogDescription",
|
|
46
|
+
inheritAttrs: false,
|
|
47
|
+
setup(_, { attrs, slots }) {
|
|
48
|
+
const { lastError } = useErrorDialogContext();
|
|
49
|
+
return () => h("p", { ...attrs }, slots["default"]?.() ?? lastError.value?.message ?? "Playback error");
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
const ErrorDialog = {
|
|
53
|
+
Root,
|
|
54
|
+
Popup: AlertDialog.Popup,
|
|
55
|
+
Title: AlertDialog.Title,
|
|
56
|
+
Description,
|
|
57
|
+
Close: AlertDialog.Close
|
|
58
|
+
};
|
|
59
|
+
//#endregion
|
|
60
|
+
export { useErrorDialogContext as n, ErrorDialog as t };
|