@hanifhan1f/vidstack 1.12.25 → 1.12.27
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/cdn/chunks/vidstack-8JHLDxl5.js +1 -0
- package/cdn/chunks/vidstack-Bpnl-N6k.js +1 -0
- package/cdn/chunks/vidstack-CnWKPIKT.js +16 -0
- package/cdn/chunks/vidstack-CqzAnF2W.js +16 -0
- package/cdn/vidstack.js +1 -1
- package/cdn/with-layouts/chunks/vidstack-BD5YoTt5.js +937 -0
- package/cdn/with-layouts/chunks/vidstack-DCaNJN4T.js +1 -0
- package/cdn/with-layouts/chunks/vidstack-Dd3L-eQj.js +1 -0
- package/cdn/with-layouts/chunks/vidstack-T2rZVigk.js +912 -0
- package/cdn/with-layouts/vidstack.js +1 -1
- package/dev/chunks/vidstack--aukHYxl.js +1520 -0
- package/dev/chunks/vidstack-B__DfQsT.js +1621 -0
- package/dev/chunks/vidstack-BoLIUOyq.js +204 -0
- package/dev/chunks/vidstack-CSryZFvY.js +1521 -0
- package/dev/chunks/vidstack-Cky9ors4.js +297 -0
- package/dev/chunks/vidstack-Crz0ROkT.js +3009 -0
- package/dev/chunks/vidstack-D-sqb6YI.js +308 -0
- package/dev/chunks/vidstack-DLXCqdYV.js +3010 -0
- package/dev/chunks/vidstack-DS7nRfge.js +204 -0
- package/dev/chunks/vidstack-Dco6kA4h.js +104 -0
- package/dev/chunks/vidstack-DpS0Kt4b.js +297 -0
- package/dev/chunks/vidstack-zJT-7ncH.js +5182 -0
- package/dev/define/plyr-layout.js +3 -4
- package/dev/define/templates/vidstack-audio-layout.js +4 -4
- package/dev/define/templates/vidstack-video-layout.js +4 -4
- package/dev/define/vidstack-player-default-layout.js +4 -4
- package/dev/define/vidstack-player-layouts.js +4 -4
- package/dev/define/vidstack-player-ui.js +5 -6
- package/dev/define/vidstack-player.js +3 -4
- package/dev/global/plyr.js +5 -6
- package/dev/global/vidstack-player.js +3 -4
- package/dev/providers/vidstack-dash.js +1 -2
- package/dev/providers/vidstack-hls.js +1 -2
- package/dev/providers/vidstack-video.js +1 -2
- package/dev/providers/vidstack-vimeo.js +1 -2
- package/dev/vidstack-elements.js +8 -9
- package/dev/vidstack.js +6 -7
- package/package.json +2 -1
- package/prod/chunks/vidstack-BVSJtdRd.js +297 -0
- package/prod/chunks/vidstack-BnEo_Sla.js +1621 -0
- package/prod/chunks/vidstack-CFXAYpuh.js +1521 -0
- package/prod/chunks/vidstack-CIvL96_j.js +297 -0
- package/prod/chunks/vidstack-CLTPjjXX.js +4772 -0
- package/prod/chunks/vidstack-CSHHV2zO.js +201 -0
- package/prod/chunks/vidstack-CYVCrFjx.js +201 -0
- package/prod/chunks/vidstack-D_atbNqH.js +3000 -0
- package/prod/chunks/vidstack-Eo46ZHu7.js +2999 -0
- package/prod/chunks/vidstack-sP7TQMB1.js +300 -0
- package/prod/chunks/vidstack-uVm3xX8H.js +104 -0
- package/prod/chunks/vidstack-zknLxihl.js +1520 -0
- package/prod/define/plyr-layout.js +3 -4
- package/prod/define/templates/vidstack-audio-layout.js +4 -4
- package/prod/define/templates/vidstack-video-layout.js +4 -4
- package/prod/define/vidstack-player-default-layout.js +4 -4
- package/prod/define/vidstack-player-layouts.js +4 -4
- package/prod/define/vidstack-player-ui.js +5 -6
- package/prod/define/vidstack-player.js +3 -4
- package/prod/global/plyr.js +5 -6
- package/prod/global/vidstack-player.js +3 -4
- package/prod/providers/vidstack-dash.js +1 -2
- package/prod/providers/vidstack-hls.js +1 -2
- package/prod/providers/vidstack-video.js +1 -2
- package/prod/providers/vidstack-vimeo.js +1 -2
- package/prod/vidstack-elements.js +8 -9
- package/prod/vidstack.js +6 -7
- package/server/chunks/vidstack-B3eA67nX.js +205 -0
- package/server/chunks/vidstack-B8P1aUCK.js +1503 -0
- package/server/chunks/vidstack-B8_v1VQn.js +3059 -0
- package/server/chunks/vidstack-BK4xGWUK.js +207 -0
- package/server/chunks/vidstack-BO8FLks6.js +295 -0
- package/server/chunks/vidstack-BaXvZgx2.js +141 -0
- package/server/chunks/vidstack-BlvJg_5A.js +4636 -0
- package/server/chunks/vidstack-CBhikwSz.js +67 -0
- package/server/chunks/vidstack-COczNXom.js +3059 -0
- package/server/chunks/vidstack-CyZPtpwO.js +1503 -0
- package/server/chunks/vidstack-Db22EuE_.js +207 -0
- package/server/chunks/vidstack-Dh1ZDEI-.js +29 -0
- package/server/chunks/vidstack-Dm-ETAZh.js +295 -0
- package/server/chunks/vidstack-NpAD9hfP.js +620 -0
- package/server/chunks/vidstack-O4BgIcQI.js +104 -0
- package/server/chunks/vidstack-n4zAyLEV.js +2139 -0
- package/server/chunks/vidstack-za5Yh5DQ.js +566 -0
- package/server/chunks/vidstack-zoXyfYxa.js +107 -0
- package/server/define/plyr-layout.js +7 -7
- package/server/define/vidstack-player-default-layout.js +3 -3
- package/server/define/vidstack-player-layouts.js +5 -5
- package/server/define/vidstack-player-ui.js +6 -6
- package/server/define/vidstack-player.js +4 -4
- package/server/global/plyr.js +9 -9
- package/server/global/vidstack-player.js +4 -4
- package/server/vidstack-elements.js +13 -13
- package/server/vidstack.js +8 -8
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
import { html } from 'lit-html';
|
|
2
|
+
import { computed, signal, effect, peek, isString, listenEvent, isKeyboardEvent, isKeyboardClick, Host, onDispose } from './vidstack-B8LynzY5.js';
|
|
3
|
+
import { usePlyrLayoutContext, PlyrLayout, usePlyrLayoutClasses } from './vidstack-zoXyfYxa.js';
|
|
4
|
+
import { useMediaContext, getDownloadFile, appendParamsToURL } from './vidstack-NpAD9hfP.js';
|
|
5
|
+
import { LayoutIconsLoader, Icon, $signal, SlotManager } from './vidstack-B3eA67nX.js';
|
|
6
|
+
import { LitElement } from './vidstack-CwTj4H1w.js';
|
|
7
|
+
import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
|
|
8
|
+
|
|
9
|
+
class PlyrLayoutIconsLoader extends LayoutIconsLoader {
|
|
10
|
+
async loadIcons() {
|
|
11
|
+
const paths = (await import('./vidstack-DXxIKXmd.js')).icons, icons = {};
|
|
12
|
+
for (const iconName of Object.keys(paths)) {
|
|
13
|
+
icons[iconName] = Icon({
|
|
14
|
+
name: iconName,
|
|
15
|
+
paths: paths[iconName],
|
|
16
|
+
viewBox: "0 0 18 18"
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return icons;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function i18n(translations, word) {
|
|
24
|
+
return translations()?.[word] ?? word;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function PlyrAudioLayout() {
|
|
28
|
+
return AudioControls();
|
|
29
|
+
}
|
|
30
|
+
function PlyrVideoLayout() {
|
|
31
|
+
const media = useMediaContext(), { load } = media.$props, { canLoad } = media.$state, showLoadScreen = computed(() => load() === "play" && !canLoad());
|
|
32
|
+
if (showLoadScreen()) {
|
|
33
|
+
return [PlayLargeButton(), Poster()];
|
|
34
|
+
}
|
|
35
|
+
return [
|
|
36
|
+
OptionalPlayLarge(),
|
|
37
|
+
PreviewScrubbing(),
|
|
38
|
+
Poster(),
|
|
39
|
+
VideoControls(),
|
|
40
|
+
Gestures(),
|
|
41
|
+
Captions()
|
|
42
|
+
];
|
|
43
|
+
}
|
|
44
|
+
function PlayLargeButton() {
|
|
45
|
+
const media = useMediaContext(), { translations } = usePlyrLayoutContext(), { title } = media.$state, $label = $signal(() => `${i18n(translations, "Play")}, ${title()}`);
|
|
46
|
+
return html`
|
|
47
|
+
<media-play-button
|
|
48
|
+
class="plyr__control plyr__control--overlaid"
|
|
49
|
+
aria-label=${$label}
|
|
50
|
+
data-plyr="play"
|
|
51
|
+
>
|
|
52
|
+
<slot name="play-icon"></slot>
|
|
53
|
+
</button>
|
|
54
|
+
`;
|
|
55
|
+
}
|
|
56
|
+
function OptionalPlayLarge() {
|
|
57
|
+
const { controls } = usePlyrLayoutContext();
|
|
58
|
+
return $signal(() => controls().includes("play-large") ? PlayLargeButton() : null);
|
|
59
|
+
}
|
|
60
|
+
function PreviewScrubbing() {
|
|
61
|
+
const { thumbnails, previewTime } = usePlyrLayoutContext();
|
|
62
|
+
return html`
|
|
63
|
+
<media-thumbnail
|
|
64
|
+
.src=${$signal(thumbnails)}
|
|
65
|
+
class="plyr__preview-scrubbing"
|
|
66
|
+
time=${$signal(() => previewTime())}
|
|
67
|
+
></media-thumbnail>
|
|
68
|
+
`;
|
|
69
|
+
}
|
|
70
|
+
function Poster() {
|
|
71
|
+
const media = useMediaContext(), { poster } = media.$state, $style = $signal(() => `background-image: url("${poster()}");`);
|
|
72
|
+
return html`<div class="plyr__poster" style=${$style}></div>`;
|
|
73
|
+
}
|
|
74
|
+
function AudioControls() {
|
|
75
|
+
const ignore = /* @__PURE__ */ new Set(["captions", "pip", "airplay", "fullscreen"]), { controls } = usePlyrLayoutContext(), $controls = $signal(
|
|
76
|
+
() => controls().filter((type) => !ignore.has(type)).map(Control)
|
|
77
|
+
);
|
|
78
|
+
return html`<div class="plyr__controls">${$controls}</div>`;
|
|
79
|
+
}
|
|
80
|
+
function VideoControls() {
|
|
81
|
+
const { controls } = usePlyrLayoutContext(), $controls = $signal(() => controls().map(Control));
|
|
82
|
+
return html`<div class="plyr__controls">${$controls}</div>`;
|
|
83
|
+
}
|
|
84
|
+
function Control(type) {
|
|
85
|
+
switch (type) {
|
|
86
|
+
case "airplay":
|
|
87
|
+
return AirPlayButton();
|
|
88
|
+
case "captions":
|
|
89
|
+
return CaptionsButton();
|
|
90
|
+
case "current-time":
|
|
91
|
+
return CurrentTime();
|
|
92
|
+
case "download":
|
|
93
|
+
return DownloadButton();
|
|
94
|
+
case "duration":
|
|
95
|
+
return Duration();
|
|
96
|
+
case "fast-forward":
|
|
97
|
+
return FastForwardButton();
|
|
98
|
+
case "fullscreen":
|
|
99
|
+
return FullscreenButton();
|
|
100
|
+
case "mute":
|
|
101
|
+
case "volume":
|
|
102
|
+
case "mute+volume":
|
|
103
|
+
return Volume(type);
|
|
104
|
+
case "pip":
|
|
105
|
+
return PIPButton();
|
|
106
|
+
case "play":
|
|
107
|
+
return PlayButton();
|
|
108
|
+
case "progress":
|
|
109
|
+
return TimeSlider();
|
|
110
|
+
case "restart":
|
|
111
|
+
return RestartButton();
|
|
112
|
+
case "rewind":
|
|
113
|
+
return RewindButton();
|
|
114
|
+
case "settings":
|
|
115
|
+
return Settings();
|
|
116
|
+
default:
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function AirPlayButton() {
|
|
121
|
+
const { translations } = usePlyrLayoutContext();
|
|
122
|
+
return html`
|
|
123
|
+
<media-airplay-button class="plyr__controls__item plyr__control" data-plyr="airplay">
|
|
124
|
+
<slot name="airplay-icon"></slot>
|
|
125
|
+
<span class="plyr__tooltip">${$i18n(translations, "AirPlay")}</span>
|
|
126
|
+
</media-airplay-button>
|
|
127
|
+
`;
|
|
128
|
+
}
|
|
129
|
+
function CaptionsButton() {
|
|
130
|
+
const { translations } = usePlyrLayoutContext(), $disableText = $i18n(translations, "Disable captions"), $enableText = $i18n(translations, "Enable captions");
|
|
131
|
+
return html`
|
|
132
|
+
<media-caption-button
|
|
133
|
+
class="plyr__controls__item plyr__control"
|
|
134
|
+
data-no-label
|
|
135
|
+
data-plyr="captions"
|
|
136
|
+
>
|
|
137
|
+
<slot name="captions-on-icon" data-class="icon--pressed"></slot>
|
|
138
|
+
<slot name="captions-off-icon" data-class="icon--not-pressed"></slot>
|
|
139
|
+
<span class="label--pressed plyr__tooltip">${$disableText}</span>
|
|
140
|
+
<span class="label--not-pressed plyr__tooltip">${$enableText}</span>
|
|
141
|
+
</media-caption-button>
|
|
142
|
+
`;
|
|
143
|
+
}
|
|
144
|
+
function FullscreenButton() {
|
|
145
|
+
const { translations } = usePlyrLayoutContext(), $enterText = $i18n(translations, "Enter Fullscreen"), $exitText = $i18n(translations, "Exit Fullscreen");
|
|
146
|
+
return html`
|
|
147
|
+
<media-fullscreen-button
|
|
148
|
+
class="plyr__controls__item plyr__control"
|
|
149
|
+
data-no-label
|
|
150
|
+
data-plyr="fullscreen"
|
|
151
|
+
>
|
|
152
|
+
<slot name="enter-fullscreen-icon" data-class="icon--pressed"></slot>
|
|
153
|
+
<slot name="exit-fullscreen-icon" data-class="icon--not-pressed"></slot>
|
|
154
|
+
<span class="label--pressed plyr__tooltip">${$exitText}</span>
|
|
155
|
+
<span class="label--not-pressed plyr__tooltip">${$enterText}</span>
|
|
156
|
+
</media-fullscreen-button>
|
|
157
|
+
`;
|
|
158
|
+
}
|
|
159
|
+
function MuteButton() {
|
|
160
|
+
const { translations } = usePlyrLayoutContext(), $muteText = $i18n(translations, "Mute"), $unmuteText = $i18n(translations, "Unmute");
|
|
161
|
+
return html`
|
|
162
|
+
<media-mute-button class="plyr__control" data-no-label data-plyr="mute">
|
|
163
|
+
<slot name="muted-icon" data-class="icon--pressed"></slot>
|
|
164
|
+
<slot name="volume-icon" data-class="icon--not-pressed"></slot>
|
|
165
|
+
<span class="label--pressed plyr__tooltip">${$unmuteText}</span>
|
|
166
|
+
<span class="label--not-pressed plyr__tooltip">${$muteText}</span>
|
|
167
|
+
</media-mute-button>
|
|
168
|
+
`;
|
|
169
|
+
}
|
|
170
|
+
function PIPButton() {
|
|
171
|
+
const { translations } = usePlyrLayoutContext(), $enterText = $i18n(translations, "Enter PiP"), $exitText = $i18n(translations, "Exit PiP");
|
|
172
|
+
return html`
|
|
173
|
+
<media-pip-button class="plyr__controls__item plyr__control" data-no-label data-plyr="pip">
|
|
174
|
+
<slot name="pip-icon"></slot>
|
|
175
|
+
<slot name="enter-pip-icon" data-class="icon--pressed"></slot>
|
|
176
|
+
<slot name="exit-pip-icon" data-class="icon--not-pressed"></slot>
|
|
177
|
+
<span class="label--pressed plyr__tooltip">${$exitText}</span>
|
|
178
|
+
<span class="label--not-pressed plyr__tooltip">${$enterText}</span>
|
|
179
|
+
</media-pip-button>
|
|
180
|
+
`;
|
|
181
|
+
}
|
|
182
|
+
function PlayButton() {
|
|
183
|
+
const { translations } = usePlyrLayoutContext(), $playText = $i18n(translations, "Play"), $pauseText = $i18n(translations, "Pause");
|
|
184
|
+
return html`
|
|
185
|
+
<media-play-button class="plyr__controls__item plyr__control" data-no-label data-plyr="play">
|
|
186
|
+
<slot name="pause-icon" data-class="icon--pressed"></slot>
|
|
187
|
+
<slot name="play-icon" data-class="icon--not-pressed"></slot>
|
|
188
|
+
<span class="label--pressed plyr__tooltip">${$pauseText}</span>
|
|
189
|
+
<span class="label--not-pressed plyr__tooltip">${$playText}</span>
|
|
190
|
+
</media-play-button>
|
|
191
|
+
`;
|
|
192
|
+
}
|
|
193
|
+
function RestartButton() {
|
|
194
|
+
const { translations } = usePlyrLayoutContext(), { remote } = useMediaContext(), $restartText = $i18n(translations, "Restart");
|
|
195
|
+
function onPress(event) {
|
|
196
|
+
if (isKeyboardEvent(event) && !isKeyboardClick(event)) return;
|
|
197
|
+
remote.seek(0, event);
|
|
198
|
+
}
|
|
199
|
+
return html`
|
|
200
|
+
<button
|
|
201
|
+
type="button"
|
|
202
|
+
class="plyr__control"
|
|
203
|
+
data-plyr="restart"
|
|
204
|
+
@pointerup=${onPress}
|
|
205
|
+
@keydown=${onPress}
|
|
206
|
+
>
|
|
207
|
+
<slot name="restart-icon"></slot>
|
|
208
|
+
<span class="plyr__tooltip">${$restartText}</span>
|
|
209
|
+
</button>
|
|
210
|
+
`;
|
|
211
|
+
}
|
|
212
|
+
function RewindButton() {
|
|
213
|
+
const { translations, seekTime } = usePlyrLayoutContext(), $label = $signal(() => `${i18n(translations, "Rewind")} ${seekTime()}s`), $seconds = $signal(() => -1 * seekTime());
|
|
214
|
+
return html`
|
|
215
|
+
<media-seek-button
|
|
216
|
+
class="plyr__controls__item plyr__control"
|
|
217
|
+
seconds=${$seconds}
|
|
218
|
+
data-no-label
|
|
219
|
+
data-plyr="rewind"
|
|
220
|
+
>
|
|
221
|
+
<slot name="rewind-icon"></slot>
|
|
222
|
+
<span class="plyr__tooltip">${$label}</span>
|
|
223
|
+
</media-seek-button>
|
|
224
|
+
`;
|
|
225
|
+
}
|
|
226
|
+
function FastForwardButton() {
|
|
227
|
+
const { translations, seekTime } = usePlyrLayoutContext(), $label = $signal(() => `${i18n(translations, "Forward")} ${seekTime()}s`), $seconds = $signal(seekTime);
|
|
228
|
+
return html`
|
|
229
|
+
<media-seek-button
|
|
230
|
+
class="plyr__controls__item plyr__control"
|
|
231
|
+
seconds=${$seconds}
|
|
232
|
+
data-no-label
|
|
233
|
+
data-plyr="fast-forward"
|
|
234
|
+
>
|
|
235
|
+
<slot name="fast-forward-icon"></slot>
|
|
236
|
+
<span class="plyr__tooltip">${$label}</span>
|
|
237
|
+
</media-seek-button>
|
|
238
|
+
`;
|
|
239
|
+
}
|
|
240
|
+
function TimeSlider() {
|
|
241
|
+
let media = useMediaContext(), { duration, viewType } = media.$state, { translations, markers, thumbnails, seekTime, previewTime } = usePlyrLayoutContext(), $seekText = $i18n(translations, "Seek"), activeMarker = signal(null), $markerLabel = $signal(() => {
|
|
242
|
+
const marker = activeMarker();
|
|
243
|
+
return marker ? html`<span class="plyr__progress__marker-label">${unsafeHTML(marker.label)}<br /></span>` : null;
|
|
244
|
+
});
|
|
245
|
+
function onSeekingRequest(event) {
|
|
246
|
+
previewTime.set(event.detail);
|
|
247
|
+
}
|
|
248
|
+
function onMarkerEnter() {
|
|
249
|
+
activeMarker.set(this);
|
|
250
|
+
}
|
|
251
|
+
function onMarkerLeave() {
|
|
252
|
+
activeMarker.set(null);
|
|
253
|
+
}
|
|
254
|
+
function Preview() {
|
|
255
|
+
const src = thumbnails(), $noClamp = $signal(() => viewType() === "audio");
|
|
256
|
+
return !src ? html`
|
|
257
|
+
<span class="plyr__tooltip">
|
|
258
|
+
${$markerLabel}
|
|
259
|
+
<media-slider-value></media-slider-value>
|
|
260
|
+
</span>
|
|
261
|
+
` : html`
|
|
262
|
+
<media-slider-preview class="plyr__slider__preview" ?no-clamp=${$noClamp}>
|
|
263
|
+
<media-slider-thumbnail .src=${src} class="plyr__slider__preview__thumbnail">
|
|
264
|
+
<span class="plyr__slider__preview__time-container">
|
|
265
|
+
${$markerLabel}
|
|
266
|
+
<media-slider-value class="plyr__slider__preview__time"></media-slider-value>
|
|
267
|
+
</span>
|
|
268
|
+
</media-slider-thumbnail>
|
|
269
|
+
</media-slider-preview>
|
|
270
|
+
`;
|
|
271
|
+
}
|
|
272
|
+
function Markers() {
|
|
273
|
+
const endTime = duration();
|
|
274
|
+
if (!Number.isFinite(endTime)) return null;
|
|
275
|
+
return markers()?.map(
|
|
276
|
+
(marker) => html`
|
|
277
|
+
<span
|
|
278
|
+
class="plyr__progress__marker"
|
|
279
|
+
@mouseenter=${onMarkerEnter.bind(marker)}
|
|
280
|
+
@mouseleave=${onMarkerLeave}
|
|
281
|
+
style=${`left: ${marker.time / endTime * 100}%;`}
|
|
282
|
+
></span>
|
|
283
|
+
`
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
return html`
|
|
287
|
+
<div class="plyr__controls__item plyr__progress__container">
|
|
288
|
+
<div class="plyr__progress">
|
|
289
|
+
<media-time-slider
|
|
290
|
+
class="plyr__slider"
|
|
291
|
+
data-plyr="seek"
|
|
292
|
+
pause-while-dragging
|
|
293
|
+
key-step=${$signal(seekTime)}
|
|
294
|
+
aria-label=${$seekText}
|
|
295
|
+
@media-seeking-request=${onSeekingRequest}
|
|
296
|
+
>
|
|
297
|
+
<div class="plyr__slider__track"></div>
|
|
298
|
+
<div class="plyr__slider__thumb"></div>
|
|
299
|
+
<div class="plyr__slider__buffer"></div>
|
|
300
|
+
${$signal(Preview)}${$signal(Markers)}
|
|
301
|
+
</media-time-slider>
|
|
302
|
+
</div>
|
|
303
|
+
</div>
|
|
304
|
+
`;
|
|
305
|
+
}
|
|
306
|
+
function Volume(type) {
|
|
307
|
+
return $signal(() => {
|
|
308
|
+
const hasMuteButton = type === "mute" || type === "mute+volume", hasVolumeSlider = type === "volume" || type === "mute+volume";
|
|
309
|
+
return html`
|
|
310
|
+
<div class="plyr__controls__item plyr__volume">
|
|
311
|
+
${[hasMuteButton ? MuteButton() : null, hasVolumeSlider ? VolumeSlider() : null]}
|
|
312
|
+
</div>
|
|
313
|
+
`;
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
function VolumeSlider() {
|
|
317
|
+
const { translations } = usePlyrLayoutContext(), $volumeText = $i18n(translations, "Volume");
|
|
318
|
+
return html`
|
|
319
|
+
<media-volume-slider class="plyr__slider" data-plyr="volume" aria-label=${$volumeText}>
|
|
320
|
+
<div class="plyr__slider__track"></div>
|
|
321
|
+
<div class="plyr__slider__thumb"></div>
|
|
322
|
+
</media-volume-slider>
|
|
323
|
+
`;
|
|
324
|
+
}
|
|
325
|
+
function CurrentTime() {
|
|
326
|
+
const media = useMediaContext(), { translations, invertTime, toggleTime, displayDuration } = usePlyrLayoutContext(), invert = signal(peek(invertTime));
|
|
327
|
+
function onPress(event) {
|
|
328
|
+
if (!toggleTime() || displayDuration() || isKeyboardEvent(event) && !isKeyboardClick(event)) {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
invert.set((n) => !n);
|
|
332
|
+
}
|
|
333
|
+
function MaybeDuration() {
|
|
334
|
+
return $signal(() => displayDuration() ? Duration() : null);
|
|
335
|
+
}
|
|
336
|
+
return $signal(() => {
|
|
337
|
+
const { streamType } = media.$state, $liveText = $i18n(translations, "LIVE"), $currentTimeText = $i18n(translations, "Current time"), $remainder = $signal(() => !displayDuration() && invert());
|
|
338
|
+
return streamType() === "live" || streamType() === "ll-live" ? html`
|
|
339
|
+
<media-live-button
|
|
340
|
+
class="plyr__controls__item plyr__control plyr__live-button"
|
|
341
|
+
data-plyr="live"
|
|
342
|
+
>
|
|
343
|
+
<span class="plyr__live-button__text">${$liveText}</span>
|
|
344
|
+
</media-live-button>
|
|
345
|
+
` : html`
|
|
346
|
+
<media-time
|
|
347
|
+
type="current"
|
|
348
|
+
class="plyr__controls__item plyr__time plyr__time--current"
|
|
349
|
+
tabindex="0"
|
|
350
|
+
role="timer"
|
|
351
|
+
aria-label=${$currentTimeText}
|
|
352
|
+
?remainder=${$remainder}
|
|
353
|
+
@pointerup=${onPress}
|
|
354
|
+
@keydown=${onPress}
|
|
355
|
+
></media-time>
|
|
356
|
+
${MaybeDuration()}
|
|
357
|
+
`;
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
function Duration() {
|
|
361
|
+
const { translations } = usePlyrLayoutContext(), $durationText = $i18n(translations, "Duration");
|
|
362
|
+
return html`
|
|
363
|
+
<media-time
|
|
364
|
+
type="duration"
|
|
365
|
+
class="plyr__controls__item plyr__time plyr__time--duration"
|
|
366
|
+
role="timer"
|
|
367
|
+
tabindex="0"
|
|
368
|
+
aria-label=${$durationText}
|
|
369
|
+
></media-time>
|
|
370
|
+
`;
|
|
371
|
+
}
|
|
372
|
+
function DownloadButton() {
|
|
373
|
+
return $signal(() => {
|
|
374
|
+
const media = useMediaContext(), { translations, download } = usePlyrLayoutContext(), { title, source } = media.$state, $src = source(), $download = download(), file = getDownloadFile({
|
|
375
|
+
title: title(),
|
|
376
|
+
src: $src,
|
|
377
|
+
download: $download
|
|
378
|
+
}), $downloadText = $i18n(translations, "Download");
|
|
379
|
+
return isString(file?.url) ? html`
|
|
380
|
+
<a
|
|
381
|
+
class="plyr__controls__item plyr__control"
|
|
382
|
+
href=${appendParamsToURL(file.url, { download: file.name })}
|
|
383
|
+
download=${file.name}
|
|
384
|
+
target="_blank"
|
|
385
|
+
>
|
|
386
|
+
<slot name="download-icon" />
|
|
387
|
+
<span class="plyr__tooltip">${$downloadText}</span>
|
|
388
|
+
</a>
|
|
389
|
+
` : null;
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
function Gestures() {
|
|
393
|
+
return $signal(() => {
|
|
394
|
+
const { clickToPlay, clickToFullscreen } = usePlyrLayoutContext();
|
|
395
|
+
return [
|
|
396
|
+
clickToPlay() ? html`
|
|
397
|
+
<media-gesture
|
|
398
|
+
class="plyr__gesture"
|
|
399
|
+
event="pointerup"
|
|
400
|
+
action="toggle:paused"
|
|
401
|
+
></media-gesture>
|
|
402
|
+
` : null,
|
|
403
|
+
clickToFullscreen() ? html`
|
|
404
|
+
<media-gesture
|
|
405
|
+
class="plyr__gesture"
|
|
406
|
+
event="dblpointerup"
|
|
407
|
+
action="toggle:fullscreen"
|
|
408
|
+
></media-gesture>
|
|
409
|
+
` : null
|
|
410
|
+
];
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
function Captions() {
|
|
414
|
+
const media = useMediaContext(), activeCue = signal(void 0), $cueText = $signal(() => unsafeHTML(activeCue()?.text));
|
|
415
|
+
effect(() => {
|
|
416
|
+
const track = media.$state.textTrack();
|
|
417
|
+
if (!track) return;
|
|
418
|
+
function onCueChange() {
|
|
419
|
+
activeCue.set(track?.activeCues[0]);
|
|
420
|
+
}
|
|
421
|
+
onCueChange();
|
|
422
|
+
return listenEvent();
|
|
423
|
+
});
|
|
424
|
+
return html`
|
|
425
|
+
<div class="plyr__captions" dir="auto">
|
|
426
|
+
<span class="plyr__caption">${$cueText}</span>
|
|
427
|
+
</div>
|
|
428
|
+
`;
|
|
429
|
+
}
|
|
430
|
+
function Settings() {
|
|
431
|
+
const { translations } = usePlyrLayoutContext(), $settingsText = $i18n(translations, "Settings");
|
|
432
|
+
return html`
|
|
433
|
+
<div class="plyr__controls__item plyr__menu">
|
|
434
|
+
<media-menu>
|
|
435
|
+
<media-menu-button class="plyr__control" data-plyr="settings">
|
|
436
|
+
<slot name="settings-icon" />
|
|
437
|
+
<span class="plyr__tooltip">${$settingsText}</span>
|
|
438
|
+
</media-menu-button>
|
|
439
|
+
<media-menu-items class="plyr__menu__container" placement="top end">
|
|
440
|
+
<div><div>${[AudioMenu(), CaptionsMenu(), QualityMenu(), SpeedMenu()]}</div></div>
|
|
441
|
+
</media-menu-items>
|
|
442
|
+
</media-menu>
|
|
443
|
+
</div>
|
|
444
|
+
`;
|
|
445
|
+
}
|
|
446
|
+
function Menu({ label, children }) {
|
|
447
|
+
const open = signal(false), onOpen = () => open.set(true), onClose = () => open.set(false);
|
|
448
|
+
return html`
|
|
449
|
+
<media-menu @open=${onOpen} @close=${onClose}>
|
|
450
|
+
${MenuButton({ label, open })}
|
|
451
|
+
<media-menu-items>${children}</media-menu-items>
|
|
452
|
+
</media-menu>
|
|
453
|
+
`;
|
|
454
|
+
}
|
|
455
|
+
function MenuButton({ open, label }) {
|
|
456
|
+
const { translations } = usePlyrLayoutContext(), $class = $signal(() => `plyr__control plyr__control--${open() ? "back" : "forward"}`);
|
|
457
|
+
function GoBackText() {
|
|
458
|
+
const $text = $i18n(translations, "Go back to previous menu");
|
|
459
|
+
return $signal(() => open() ? html`<span class="plyr__sr-only">${$text}</span>` : null);
|
|
460
|
+
}
|
|
461
|
+
return html`
|
|
462
|
+
<media-menu-button class=${$class} data-plyr="settings">
|
|
463
|
+
<span class="plyr__menu__label" aria-hidden=${$aria(open)}>
|
|
464
|
+
${$i18n(translations, label)}
|
|
465
|
+
</span>
|
|
466
|
+
<span class="plyr__menu__value" data-part="hint"></span>
|
|
467
|
+
${GoBackText()}
|
|
468
|
+
</media-menu-button>
|
|
469
|
+
`;
|
|
470
|
+
}
|
|
471
|
+
function AudioMenu() {
|
|
472
|
+
return Menu({ label: "Audio", children: AudioRadioGroup() });
|
|
473
|
+
}
|
|
474
|
+
function AudioRadioGroup() {
|
|
475
|
+
const { translations } = usePlyrLayoutContext();
|
|
476
|
+
return html`
|
|
477
|
+
<media-audio-radio-group empty-label=${$i18n(translations, "Default")}>
|
|
478
|
+
<template>
|
|
479
|
+
<media-radio class="plyr__control" data-plyr="audio">
|
|
480
|
+
<span data-part="label"></span>
|
|
481
|
+
</media-radio>
|
|
482
|
+
</template>
|
|
483
|
+
</media-audio-radio-group>
|
|
484
|
+
`;
|
|
485
|
+
}
|
|
486
|
+
function SpeedMenu() {
|
|
487
|
+
return Menu({ label: "Speed", children: SpeedRadioGroup() });
|
|
488
|
+
}
|
|
489
|
+
function SpeedRadioGroup() {
|
|
490
|
+
const { translations, speed } = usePlyrLayoutContext();
|
|
491
|
+
return html`
|
|
492
|
+
<media-speed-radio-group .rates=${speed} normal-label=${$i18n(translations, "Normal")}>
|
|
493
|
+
<template>
|
|
494
|
+
<media-radio class="plyr__control" data-plyr="speed">
|
|
495
|
+
<span data-part="label"></span>
|
|
496
|
+
</media-radio>
|
|
497
|
+
</template>
|
|
498
|
+
</media-speed-radio-group>
|
|
499
|
+
`;
|
|
500
|
+
}
|
|
501
|
+
function CaptionsMenu() {
|
|
502
|
+
return Menu({ label: "Captions", children: CaptionsRadioGroup() });
|
|
503
|
+
}
|
|
504
|
+
function CaptionsRadioGroup() {
|
|
505
|
+
const { translations } = usePlyrLayoutContext();
|
|
506
|
+
return html`
|
|
507
|
+
<media-captions-radio-group off-label=${$i18n(translations, "Disabled")}>
|
|
508
|
+
<template>
|
|
509
|
+
<media-radio class="plyr__control" data-plyr="captions">
|
|
510
|
+
<span data-part="label"></span>
|
|
511
|
+
</media-radio>
|
|
512
|
+
</template>
|
|
513
|
+
</media-captions-radio-group>
|
|
514
|
+
`;
|
|
515
|
+
}
|
|
516
|
+
function QualityMenu() {
|
|
517
|
+
return Menu({ label: "Quality", children: QualityRadioGroup() });
|
|
518
|
+
}
|
|
519
|
+
function QualityRadioGroup() {
|
|
520
|
+
const { translations } = usePlyrLayoutContext();
|
|
521
|
+
return html`
|
|
522
|
+
<media-quality-radio-group auto-label=${$i18n(translations, "Auto")}>
|
|
523
|
+
<template>
|
|
524
|
+
<media-radio class="plyr__control" data-plyr="quality">
|
|
525
|
+
<span data-part="label"></span>
|
|
526
|
+
</media-radio>
|
|
527
|
+
</template>
|
|
528
|
+
</media-quality-radio-group>
|
|
529
|
+
`;
|
|
530
|
+
}
|
|
531
|
+
function $aria(signal2) {
|
|
532
|
+
return $signal(() => signal2() ? "true" : "false");
|
|
533
|
+
}
|
|
534
|
+
function $i18n(translations, word) {
|
|
535
|
+
return $signal(() => i18n(translations, word));
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
class MediaPlyrLayoutElement extends Host(LitElement, PlyrLayout) {
|
|
539
|
+
static tagName = "media-plyr-layout";
|
|
540
|
+
#media;
|
|
541
|
+
onSetup() {
|
|
542
|
+
this.forwardKeepAlive = false;
|
|
543
|
+
this.#media = useMediaContext();
|
|
544
|
+
}
|
|
545
|
+
onConnect() {
|
|
546
|
+
this.#media.player.el?.setAttribute("data-layout", "plyr");
|
|
547
|
+
onDispose(() => this.#media.player.el?.removeAttribute("data-layout"));
|
|
548
|
+
usePlyrLayoutClasses(this, this.#media);
|
|
549
|
+
effect(() => {
|
|
550
|
+
if (this.$props.customIcons()) {
|
|
551
|
+
new SlotManager([this]).connect();
|
|
552
|
+
} else {
|
|
553
|
+
new PlyrLayoutIconsLoader([this]).connect();
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
render() {
|
|
558
|
+
return $signal(this.#render.bind(this));
|
|
559
|
+
}
|
|
560
|
+
#render() {
|
|
561
|
+
const { viewType } = this.#media.$state;
|
|
562
|
+
return viewType() === "audio" ? PlyrAudioLayout() : viewType() === "video" ? PlyrVideoLayout() : null;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
export { MediaPlyrLayoutElement };
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { createContext, useContext, Component, provideContext, signal, effect, createDisposalBin } from './vidstack-B8LynzY5.js';
|
|
2
|
+
import { useMediaContext } from './vidstack-NpAD9hfP.js';
|
|
3
|
+
|
|
4
|
+
const plyrLayoutContext = createContext();
|
|
5
|
+
function usePlyrLayoutContext() {
|
|
6
|
+
return useContext(plyrLayoutContext);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const plyrLayoutProps = {
|
|
10
|
+
clickToPlay: true,
|
|
11
|
+
clickToFullscreen: true,
|
|
12
|
+
controls: [
|
|
13
|
+
"play-large",
|
|
14
|
+
"play",
|
|
15
|
+
"progress",
|
|
16
|
+
"current-time",
|
|
17
|
+
"mute+volume",
|
|
18
|
+
"captions",
|
|
19
|
+
"settings",
|
|
20
|
+
"pip",
|
|
21
|
+
"airplay",
|
|
22
|
+
"fullscreen"
|
|
23
|
+
],
|
|
24
|
+
customIcons: false,
|
|
25
|
+
displayDuration: false,
|
|
26
|
+
download: null,
|
|
27
|
+
markers: null,
|
|
28
|
+
invertTime: true,
|
|
29
|
+
thumbnails: null,
|
|
30
|
+
toggleTime: true,
|
|
31
|
+
translations: null,
|
|
32
|
+
seekTime: 10,
|
|
33
|
+
speed: [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 4]
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
class PlyrLayout extends Component {
|
|
37
|
+
static props = plyrLayoutProps;
|
|
38
|
+
#media;
|
|
39
|
+
onSetup() {
|
|
40
|
+
this.#media = useMediaContext();
|
|
41
|
+
provideContext(plyrLayoutContext, {
|
|
42
|
+
...this.$props,
|
|
43
|
+
previewTime: signal(0)
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function usePlyrLayoutClasses(el, media) {
|
|
48
|
+
const {
|
|
49
|
+
canAirPlay,
|
|
50
|
+
canFullscreen,
|
|
51
|
+
canPictureInPicture,
|
|
52
|
+
controlsHidden,
|
|
53
|
+
currentTime,
|
|
54
|
+
fullscreen,
|
|
55
|
+
hasCaptions,
|
|
56
|
+
isAirPlayConnected,
|
|
57
|
+
paused,
|
|
58
|
+
pictureInPicture,
|
|
59
|
+
playing,
|
|
60
|
+
pointer,
|
|
61
|
+
poster,
|
|
62
|
+
textTrack,
|
|
63
|
+
viewType,
|
|
64
|
+
waiting
|
|
65
|
+
} = media.$state;
|
|
66
|
+
el.classList.add("plyr");
|
|
67
|
+
el.classList.add("plyr--full-ui");
|
|
68
|
+
const classes = {
|
|
69
|
+
"plyr--airplay-active": isAirPlayConnected,
|
|
70
|
+
"plyr--airplay-supported": canAirPlay,
|
|
71
|
+
"plyr--fullscreen-active": fullscreen,
|
|
72
|
+
"plyr--fullscreen-enabled": canFullscreen,
|
|
73
|
+
"plyr--hide-controls": controlsHidden,
|
|
74
|
+
"plyr--is-touch": () => pointer() === "coarse",
|
|
75
|
+
"plyr--loading": waiting,
|
|
76
|
+
"plyr--paused": paused,
|
|
77
|
+
"plyr--pip-active": pictureInPicture,
|
|
78
|
+
"plyr--pip-enabled": canPictureInPicture,
|
|
79
|
+
"plyr--playing": playing,
|
|
80
|
+
"plyr__poster-enabled": poster,
|
|
81
|
+
"plyr--stopped": () => paused() && currentTime() === 0,
|
|
82
|
+
"plyr--captions-active": textTrack,
|
|
83
|
+
"plyr--captions-enabled": hasCaptions
|
|
84
|
+
};
|
|
85
|
+
const disposal = createDisposalBin();
|
|
86
|
+
for (const token of Object.keys(classes)) {
|
|
87
|
+
disposal.add(effect(() => void el.classList.toggle(token, !!classes[token]())));
|
|
88
|
+
}
|
|
89
|
+
disposal.add(
|
|
90
|
+
effect(() => {
|
|
91
|
+
const token = `plyr--${viewType()}`;
|
|
92
|
+
el.classList.add(token);
|
|
93
|
+
return () => el.classList.remove(token);
|
|
94
|
+
}),
|
|
95
|
+
effect(() => {
|
|
96
|
+
const { $provider } = media, type = $provider()?.type, token = `plyr--${isHTMLProvider(type) ? "html5" : type}`;
|
|
97
|
+
el.classList.toggle(token, !!type);
|
|
98
|
+
return () => el.classList.remove(token);
|
|
99
|
+
})
|
|
100
|
+
);
|
|
101
|
+
return () => disposal.empty();
|
|
102
|
+
}
|
|
103
|
+
function isHTMLProvider(type) {
|
|
104
|
+
return type === "audio" || type === "video";
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export { PlyrLayout, usePlyrLayoutClasses, usePlyrLayoutContext };
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import '../chunks/vidstack-
|
|
2
|
-
import '../chunks/vidstack-
|
|
3
|
-
import '../chunks/vidstack-
|
|
1
|
+
import '../chunks/vidstack-BK4xGWUK.js';
|
|
2
|
+
import '../chunks/vidstack-za5Yh5DQ.js';
|
|
3
|
+
import '../chunks/vidstack-COczNXom.js';
|
|
4
4
|
import '../chunks/vidstack-B8LynzY5.js';
|
|
5
|
-
import '../chunks/vidstack-
|
|
5
|
+
import '../chunks/vidstack-NpAD9hfP.js';
|
|
6
6
|
import '@floating-ui/dom';
|
|
7
7
|
import 'lit-html';
|
|
8
|
-
import '../chunks/vidstack-
|
|
9
|
-
import '../chunks/vidstack-
|
|
8
|
+
import '../chunks/vidstack-zoXyfYxa.js';
|
|
9
|
+
import '../chunks/vidstack-B3eA67nX.js';
|
|
10
10
|
import 'lit-html/directives/if-defined.js';
|
|
11
11
|
import 'lit-html/directives/unsafe-svg.js';
|
|
12
12
|
import 'lit-html/async-directive.js';
|
|
13
13
|
import '../chunks/vidstack-CwTj4H1w.js';
|
|
14
14
|
import 'lit-html/directives/unsafe-html.js';
|
|
15
15
|
import '../chunks/vidstack-BOTZD4tC.js';
|
|
16
|
-
import '../chunks/vidstack-
|
|
16
|
+
import '../chunks/vidstack-CBhikwSz.js';
|