ts-video-player 0.1.3 → 0.1.5
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/dist/cdn.min.js +32 -32
- package/dist/{chunk-yfrrczn9.js → chunk-z21ez1kz.js} +77 -1
- package/dist/core/cover.d.ts +23 -0
- package/dist/elements/index.js +1 -1
- package/dist/index.js +1 -1
- package/dist/player.d.ts +17 -0
- package/dist/types.d.ts +2 -0
- package/package.json +1 -1
- package/src/core/cover.ts +42 -0
- package/src/elements/video-player.ts +3 -0
- package/src/player.ts +76 -1
- package/src/providers/vimeo.ts +2 -0
- package/src/providers/youtube.ts +2 -0
- package/src/types.ts +2 -0
|
@@ -666,6 +666,7 @@ class YouTubeProvider2 extends BaseProvider2 {
|
|
|
666
666
|
});
|
|
667
667
|
this.iframe = this.player.getIframe();
|
|
668
668
|
this.iframe.className = "ts-video-player__media";
|
|
669
|
+
this.iframe.style.cssText = "position:absolute;inset:0;width:100%;height:100%;border:0";
|
|
669
670
|
}
|
|
670
671
|
async loadYouTubeAPI() {
|
|
671
672
|
if (window.YT?.Player)
|
|
@@ -1028,6 +1029,7 @@ class VimeoProvider2 extends BaseProvider2 {
|
|
|
1028
1029
|
this.iframe = this.container_el.querySelector("iframe");
|
|
1029
1030
|
if (this.iframe) {
|
|
1030
1031
|
this.iframe.className = "ts-video-player__media";
|
|
1032
|
+
this.iframe.style.cssText = "position:absolute;inset:0;width:100%;height:100%;border:0";
|
|
1031
1033
|
}
|
|
1032
1034
|
this.attachVimeoEvents();
|
|
1033
1035
|
this.events.emit("loadedmetadata");
|
|
@@ -2007,6 +2009,19 @@ function getPreconnectHints2(src, loaders = defaultLoaders2) {
|
|
|
2007
2009
|
return loader?.preconnect?.() || [];
|
|
2008
2010
|
}
|
|
2009
2011
|
|
|
2012
|
+
// src/core/cover.ts
|
|
2013
|
+
function embedCover(state, dismissed) {
|
|
2014
|
+
const embed = state.providerType === "youtube" || state.providerType === "vimeo";
|
|
2015
|
+
if (!embed || state.started || state.error || dismissed)
|
|
2016
|
+
return { show: false, image: "" };
|
|
2017
|
+
if (state.poster)
|
|
2018
|
+
return { show: true, image: state.poster };
|
|
2019
|
+
const src = state.src;
|
|
2020
|
+
const url = typeof src === "string" ? src : src?.src || "";
|
|
2021
|
+
const youtubeId = state.providerType === "youtube" ? extractYouTubeId2(url) : null;
|
|
2022
|
+
return { show: true, image: youtubeId ? `https://i.ytimg.com/vi/${youtubeId}/hqdefault.jpg` : "" };
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2010
2025
|
// src/player.ts
|
|
2011
2026
|
class Player2 {
|
|
2012
2027
|
_el;
|
|
@@ -2019,6 +2034,9 @@ class Player2 {
|
|
|
2019
2034
|
_destroyed = false;
|
|
2020
2035
|
_sourceLoadId = 0;
|
|
2021
2036
|
_announcer = null;
|
|
2037
|
+
_cover = null;
|
|
2038
|
+
_coverDismissed = false;
|
|
2039
|
+
_coverTimer = null;
|
|
2022
2040
|
constructor(container, options = {}) {
|
|
2023
2041
|
if (typeof container === "string") {
|
|
2024
2042
|
const el = document.querySelector(container);
|
|
@@ -2064,7 +2082,9 @@ class Player2 {
|
|
|
2064
2082
|
this._el.setAttribute("aria-keyshortcuts", "Space K M F I C ArrowLeft ArrowRight J L ArrowUp ArrowDown Home End 0 1 2 3 4 5 6 7 8 9");
|
|
2065
2083
|
const mediaContainer = document.createElement("div");
|
|
2066
2084
|
mediaContainer.className = "ts-video-player__container";
|
|
2085
|
+
mediaContainer.style.cssText = "position:relative;width:100%;height:100%";
|
|
2067
2086
|
this._el.appendChild(mediaContainer);
|
|
2087
|
+
this.setupCover();
|
|
2068
2088
|
if (this._options.announcements !== false) {
|
|
2069
2089
|
const liveRegion = document.createElement("div");
|
|
2070
2090
|
liveRegion.className = "ts-video-player__announcer";
|
|
@@ -2078,6 +2098,53 @@ class Player2 {
|
|
|
2078
2098
|
const _el = this._el;
|
|
2079
2099
|
_el.__videoPlayer = this;
|
|
2080
2100
|
}
|
|
2101
|
+
setupCover() {
|
|
2102
|
+
const cover = document.createElement("button");
|
|
2103
|
+
cover.type = "button";
|
|
2104
|
+
cover.className = "ts-video-player__cover";
|
|
2105
|
+
cover.setAttribute("aria-label", `Play ${this._options.title || "video"}`);
|
|
2106
|
+
cover.hidden = true;
|
|
2107
|
+
cover.style.cssText = "position:absolute;inset:0;z-index:2;display:none;align-items:center;justify-content:center;width:100%;height:100%;margin:0;padding:0;border:0;border-radius:inherit;background:#000 center/cover no-repeat;cursor:pointer;-webkit-tap-highlight-color:transparent";
|
|
2108
|
+
const button = document.createElement("span");
|
|
2109
|
+
button.setAttribute("aria-hidden", "true");
|
|
2110
|
+
button.style.cssText = "display:flex;align-items:center;justify-content:center;width:64px;height:64px;border-radius:50%;background:rgba(255,255,255,0.92);box-shadow:0 4px 18px rgba(0,0,0,0.35)";
|
|
2111
|
+
const triangle = document.createElement("span");
|
|
2112
|
+
triangle.style.cssText = "width:22px;height:24px;margin-left:5px;background:#0f172a;clip-path:polygon(0 0,100% 50%,0 100%)";
|
|
2113
|
+
button.appendChild(triangle);
|
|
2114
|
+
cover.appendChild(button);
|
|
2115
|
+
cover.addEventListener("click", (event) => {
|
|
2116
|
+
event.stopPropagation();
|
|
2117
|
+
this.play().catch(() => {});
|
|
2118
|
+
if (this._coverTimer)
|
|
2119
|
+
clearTimeout(this._coverTimer);
|
|
2120
|
+
this._coverTimer = setTimeout(() => {
|
|
2121
|
+
if (this.state.started)
|
|
2122
|
+
return;
|
|
2123
|
+
this._coverDismissed = true;
|
|
2124
|
+
this.updateCover(this.state);
|
|
2125
|
+
}, 2000);
|
|
2126
|
+
});
|
|
2127
|
+
this._el.appendChild(cover);
|
|
2128
|
+
this._cover = cover;
|
|
2129
|
+
this._cleanupFns.push(() => {
|
|
2130
|
+
if (this._coverTimer)
|
|
2131
|
+
clearTimeout(this._coverTimer);
|
|
2132
|
+
cover.remove();
|
|
2133
|
+
});
|
|
2134
|
+
}
|
|
2135
|
+
updateCover(state) {
|
|
2136
|
+
const cover = this._cover;
|
|
2137
|
+
if (!cover)
|
|
2138
|
+
return;
|
|
2139
|
+
const { show, image } = embedCover(state, this._coverDismissed);
|
|
2140
|
+
if (show) {
|
|
2141
|
+
const background = image ? `url("${image.replace(/["\\]/g, "")}")` : "none";
|
|
2142
|
+
if (cover.style.backgroundImage !== background)
|
|
2143
|
+
cover.style.backgroundImage = background;
|
|
2144
|
+
}
|
|
2145
|
+
cover.hidden = !show;
|
|
2146
|
+
cover.style.display = show ? "flex" : "none";
|
|
2147
|
+
}
|
|
2081
2148
|
setupState() {
|
|
2082
2149
|
this._store.batch({
|
|
2083
2150
|
volume: this._options.volume ?? 1,
|
|
@@ -2092,6 +2159,7 @@ class Player2 {
|
|
|
2092
2159
|
});
|
|
2093
2160
|
const unsubscribe = this._store.subscribe((state) => {
|
|
2094
2161
|
this.updateAttributes(state);
|
|
2162
|
+
this.updateCover(state);
|
|
2095
2163
|
});
|
|
2096
2164
|
this._cleanupFns.push(unsubscribe);
|
|
2097
2165
|
}
|
|
@@ -2235,11 +2303,13 @@ class Player2 {
|
|
|
2235
2303
|
if (sources.length === 0) {
|
|
2236
2304
|
return;
|
|
2237
2305
|
}
|
|
2306
|
+
this._coverDismissed = false;
|
|
2238
2307
|
this._store.batch({
|
|
2239
2308
|
sources,
|
|
2240
2309
|
src: sources[0],
|
|
2241
2310
|
loadingState: "loading",
|
|
2242
|
-
error: null
|
|
2311
|
+
error: null,
|
|
2312
|
+
started: false
|
|
2243
2313
|
});
|
|
2244
2314
|
this._events.emit("sourceschange", sources);
|
|
2245
2315
|
const candidates = findSourceCandidates(sources, this._loaders);
|
|
@@ -2308,6 +2378,9 @@ class Player2 {
|
|
|
2308
2378
|
error: { code: 4, message: "Failed to load all compatible sources", details: lastError }
|
|
2309
2379
|
});
|
|
2310
2380
|
}
|
|
2381
|
+
setPoster(url) {
|
|
2382
|
+
this._store.set("poster", url);
|
|
2383
|
+
}
|
|
2311
2384
|
getSrc() {
|
|
2312
2385
|
return this.state.src;
|
|
2313
2386
|
}
|
|
@@ -2626,6 +2699,9 @@ class VideoPlayerElement2 extends HTMLElementBase {
|
|
|
2626
2699
|
if (value)
|
|
2627
2700
|
this._player.setSrc(value);
|
|
2628
2701
|
break;
|
|
2702
|
+
case "poster":
|
|
2703
|
+
this._player.setPoster(value || "");
|
|
2704
|
+
break;
|
|
2629
2705
|
case "muted":
|
|
2630
2706
|
this._player.setMuted(value !== null);
|
|
2631
2707
|
break;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The poster over an embedded video until it plays: when to show it, and
|
|
3
|
+
* with which image.
|
|
4
|
+
*
|
|
5
|
+
* @module core/cover
|
|
6
|
+
*/
|
|
7
|
+
import type { PlayerState } from '../types';
|
|
8
|
+
export interface EmbedCover {
|
|
9
|
+
show: boolean;
|
|
10
|
+
/** The poster to draw, or '' for a plain black cover. */
|
|
11
|
+
image: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A YouTube or Vimeo embed draws its own chrome before playback (a title bar,
|
|
15
|
+
* a play button, "Watch on YouTube", and on iOS the system's controls for the
|
|
16
|
+
* embed's own video), none of which can be styled from outside the iframe.
|
|
17
|
+
* Until the first play the player covers it with its poster instead: the one
|
|
18
|
+
* the page gave it, or else YouTube's thumbnail for the video.
|
|
19
|
+
*
|
|
20
|
+
* `dismissed` is set when a tap on the cover did not start playback, so the
|
|
21
|
+
* embed's own button is left reachable.
|
|
22
|
+
*/
|
|
23
|
+
export declare function embedCover(state: Pick<PlayerState, 'providerType' | 'started' | 'error' | 'poster' | 'src'>, dismissed: boolean): EmbedCover;
|
package/dist/elements/index.js
CHANGED
package/dist/index.js
CHANGED
package/dist/player.d.ts
CHANGED
|
@@ -20,6 +20,9 @@ export declare class Player implements IPlayer {
|
|
|
20
20
|
private _destroyed;
|
|
21
21
|
private _sourceLoadId;
|
|
22
22
|
private _announcer;
|
|
23
|
+
private _cover;
|
|
24
|
+
private _coverDismissed;
|
|
25
|
+
private _coverTimer;
|
|
23
26
|
/**
|
|
24
27
|
* Create a new player instance
|
|
25
28
|
*/
|
|
@@ -29,6 +32,18 @@ export declare class Player implements IPlayer {
|
|
|
29
32
|
get provider(): Provider | null;
|
|
30
33
|
get ready(): boolean;
|
|
31
34
|
private setupElement;
|
|
35
|
+
/**
|
|
36
|
+
* The poster over an embedded video until it plays.
|
|
37
|
+
*
|
|
38
|
+
* A YouTube or Vimeo embed draws its own chrome before playback: a title
|
|
39
|
+
* bar, a play button, "Watch on YouTube", and on iOS the system's controls
|
|
40
|
+
* for the embed's own video. None of it can be styled from outside the
|
|
41
|
+
* iframe, and it stacks under the player's controls. Until the first play,
|
|
42
|
+
* the player shows its poster and one play button over the embed instead;
|
|
43
|
+
* once the video plays the embed's chrome is gone and the cover lifts.
|
|
44
|
+
*/
|
|
45
|
+
private setupCover;
|
|
46
|
+
private updateCover;
|
|
32
47
|
private setupState;
|
|
33
48
|
private setupKeyboard;
|
|
34
49
|
private setupActivity;
|
|
@@ -42,6 +57,8 @@ export declare class Player implements IPlayer {
|
|
|
42
57
|
private announce;
|
|
43
58
|
destroy(): void;
|
|
44
59
|
setSrc(src: Src | Src[]): Promise<void>;
|
|
60
|
+
/** The image shown before playback, and on an embed's cover. */
|
|
61
|
+
setPoster(url: string): void;
|
|
45
62
|
getSrc(): Src | null;
|
|
46
63
|
private attachProviderEvents;
|
|
47
64
|
play(): Promise<void>;
|
package/dist/types.d.ts
CHANGED
|
@@ -632,6 +632,8 @@ export interface Player {
|
|
|
632
632
|
destroy(): void;
|
|
633
633
|
setSrc(src: Src | Src[]): void;
|
|
634
634
|
getSrc(): Src | null;
|
|
635
|
+
/** The image shown before playback, and on an embed's cover */
|
|
636
|
+
setPoster(url: string): void;
|
|
635
637
|
play(): Promise<void>;
|
|
636
638
|
pause(): void;
|
|
637
639
|
togglePlay(): void;
|
package/package.json
CHANGED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The poster over an embedded video until it plays: when to show it, and
|
|
3
|
+
* with which image.
|
|
4
|
+
*
|
|
5
|
+
* @module core/cover
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { PlayerState } from '../types'
|
|
9
|
+
import { extractYouTubeId } from '../providers/youtube'
|
|
10
|
+
|
|
11
|
+
export interface EmbedCover {
|
|
12
|
+
show: boolean
|
|
13
|
+
/** The poster to draw, or '' for a plain black cover. */
|
|
14
|
+
image: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A YouTube or Vimeo embed draws its own chrome before playback (a title bar,
|
|
19
|
+
* a play button, "Watch on YouTube", and on iOS the system's controls for the
|
|
20
|
+
* embed's own video), none of which can be styled from outside the iframe.
|
|
21
|
+
* Until the first play the player covers it with its poster instead: the one
|
|
22
|
+
* the page gave it, or else YouTube's thumbnail for the video.
|
|
23
|
+
*
|
|
24
|
+
* `dismissed` is set when a tap on the cover did not start playback, so the
|
|
25
|
+
* embed's own button is left reachable.
|
|
26
|
+
*/
|
|
27
|
+
export function embedCover(
|
|
28
|
+
state: Pick<PlayerState, 'providerType' | 'started' | 'error' | 'poster' | 'src'>,
|
|
29
|
+
dismissed: boolean,
|
|
30
|
+
): EmbedCover {
|
|
31
|
+
const embed = state.providerType === 'youtube' || state.providerType === 'vimeo'
|
|
32
|
+
if (!embed || state.started || state.error || dismissed)
|
|
33
|
+
return { show: false, image: '' }
|
|
34
|
+
|
|
35
|
+
if (state.poster)
|
|
36
|
+
return { show: true, image: state.poster }
|
|
37
|
+
|
|
38
|
+
const src = state.src
|
|
39
|
+
const url = typeof src === 'string' ? src : src?.src || ''
|
|
40
|
+
const youtubeId = state.providerType === 'youtube' ? extractYouTubeId(url) : null
|
|
41
|
+
return { show: true, image: youtubeId ? `https://i.ytimg.com/vi/${youtubeId}/hqdefault.jpg` : '' }
|
|
42
|
+
}
|
package/src/player.ts
CHANGED
|
@@ -21,6 +21,7 @@ import { EventEmitter, createKeyboardHandler, createActivityDetector, type Keybo
|
|
|
21
21
|
import { createMediaSession, type MediaSessionOptions } from './core/media-session'
|
|
22
22
|
import { lockOrientation, unlockOrientation } from './core/orientation'
|
|
23
23
|
import { defaultLoaders, findSourceCandidates } from './providers'
|
|
24
|
+
import { embedCover } from './core/cover'
|
|
24
25
|
|
|
25
26
|
// =============================================================================
|
|
26
27
|
// Player Class
|
|
@@ -40,6 +41,9 @@ export class Player implements IPlayer {
|
|
|
40
41
|
private _destroyed = false
|
|
41
42
|
private _sourceLoadId = 0
|
|
42
43
|
private _announcer: HTMLElement | null = null
|
|
44
|
+
private _cover: HTMLButtonElement | null = null
|
|
45
|
+
private _coverDismissed = false
|
|
46
|
+
private _coverTimer: ReturnType<typeof setTimeout> | null = null
|
|
43
47
|
|
|
44
48
|
/**
|
|
45
49
|
* Create a new player instance
|
|
@@ -108,8 +112,14 @@ export class Player implements IPlayer {
|
|
|
108
112
|
// Create inner structure
|
|
109
113
|
const mediaContainer = document.createElement('div')
|
|
110
114
|
mediaContainer.className = 'ts-video-player__container'
|
|
115
|
+
// Sized here, not only in a stylesheet: inside <video-player>'s shadow
|
|
116
|
+
// root no page stylesheet reaches it, and an embed's iframe sizes itself
|
|
117
|
+
// from this box.
|
|
118
|
+
mediaContainer.style.cssText = 'position:relative;width:100%;height:100%'
|
|
111
119
|
this._el.appendChild(mediaContainer)
|
|
112
120
|
|
|
121
|
+
this.setupCover()
|
|
122
|
+
|
|
113
123
|
// Create ARIA live region for screen reader announcements
|
|
114
124
|
if (this._options.announcements !== false) {
|
|
115
125
|
const liveRegion = document.createElement('div')
|
|
@@ -128,6 +138,63 @@ export class Player implements IPlayer {
|
|
|
128
138
|
_el.__videoPlayer = this
|
|
129
139
|
}
|
|
130
140
|
|
|
141
|
+
/**
|
|
142
|
+
* The poster over an embedded video until it plays.
|
|
143
|
+
*
|
|
144
|
+
* A YouTube or Vimeo embed draws its own chrome before playback: a title
|
|
145
|
+
* bar, a play button, "Watch on YouTube", and on iOS the system's controls
|
|
146
|
+
* for the embed's own video. None of it can be styled from outside the
|
|
147
|
+
* iframe, and it stacks under the player's controls. Until the first play,
|
|
148
|
+
* the player shows its poster and one play button over the embed instead;
|
|
149
|
+
* once the video plays the embed's chrome is gone and the cover lifts.
|
|
150
|
+
*/
|
|
151
|
+
private setupCover(): void {
|
|
152
|
+
const cover = document.createElement('button')
|
|
153
|
+
cover.type = 'button'
|
|
154
|
+
cover.className = 'ts-video-player__cover'
|
|
155
|
+
cover.setAttribute('aria-label', `Play ${this._options.title || 'video'}`)
|
|
156
|
+
cover.hidden = true
|
|
157
|
+
cover.style.cssText = 'position:absolute;inset:0;z-index:2;display:none;align-items:center;justify-content:center;width:100%;height:100%;margin:0;padding:0;border:0;border-radius:inherit;background:#000 center/cover no-repeat;cursor:pointer;-webkit-tap-highlight-color:transparent'
|
|
158
|
+
const button = document.createElement('span')
|
|
159
|
+
button.setAttribute('aria-hidden', 'true')
|
|
160
|
+
button.style.cssText = 'display:flex;align-items:center;justify-content:center;width:64px;height:64px;border-radius:50%;background:rgba(255,255,255,0.92);box-shadow:0 4px 18px rgba(0,0,0,0.35)'
|
|
161
|
+
const triangle = document.createElement('span')
|
|
162
|
+
triangle.style.cssText = 'width:22px;height:24px;margin-left:5px;background:#0f172a;clip-path:polygon(0 0,100% 50%,0 100%)'
|
|
163
|
+
button.appendChild(triangle)
|
|
164
|
+
cover.appendChild(button)
|
|
165
|
+
cover.addEventListener('click', (event) => {
|
|
166
|
+
event.stopPropagation()
|
|
167
|
+
void this.play().catch(() => {})
|
|
168
|
+
// Some browsers only let a tap inside the embed start it. If this one
|
|
169
|
+
// did not, get out of the way so the embed's own button can.
|
|
170
|
+
if (this._coverTimer) clearTimeout(this._coverTimer)
|
|
171
|
+
this._coverTimer = setTimeout(() => {
|
|
172
|
+
if (this.state.started) return
|
|
173
|
+
this._coverDismissed = true
|
|
174
|
+
this.updateCover(this.state)
|
|
175
|
+
}, 2000)
|
|
176
|
+
})
|
|
177
|
+
this._el.appendChild(cover)
|
|
178
|
+
this._cover = cover
|
|
179
|
+
this._cleanupFns.push(() => {
|
|
180
|
+
if (this._coverTimer) clearTimeout(this._coverTimer)
|
|
181
|
+
cover.remove()
|
|
182
|
+
})
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private updateCover(state: PlayerState): void {
|
|
186
|
+
const cover = this._cover
|
|
187
|
+
if (!cover) return
|
|
188
|
+
const { show, image } = embedCover(state, this._coverDismissed)
|
|
189
|
+
if (show) {
|
|
190
|
+
const background = image ? `url("${image.replace(/["\\]/g, '')}")` : 'none'
|
|
191
|
+
if (cover.style.backgroundImage !== background) cover.style.backgroundImage = background
|
|
192
|
+
}
|
|
193
|
+
// `hidden` alone would not do it: the inline display wins over it.
|
|
194
|
+
cover.hidden = !show
|
|
195
|
+
cover.style.display = show ? 'flex' : 'none'
|
|
196
|
+
}
|
|
197
|
+
|
|
131
198
|
private setupState(): void {
|
|
132
199
|
// Initial state from options
|
|
133
200
|
this._store.batch({
|
|
@@ -146,6 +213,7 @@ export class Player implements IPlayer {
|
|
|
146
213
|
const unsubscribe = this._store.subscribe((state) => {
|
|
147
214
|
// Update DOM attributes
|
|
148
215
|
this.updateAttributes(state)
|
|
216
|
+
this.updateCover(state)
|
|
149
217
|
})
|
|
150
218
|
|
|
151
219
|
this._cleanupFns.push(unsubscribe)
|
|
@@ -362,12 +430,14 @@ export class Player implements IPlayer {
|
|
|
362
430
|
return
|
|
363
431
|
}
|
|
364
432
|
|
|
365
|
-
// Update state
|
|
433
|
+
// Update state. A new source has not played yet, so its cover returns.
|
|
434
|
+
this._coverDismissed = false
|
|
366
435
|
this._store.batch({
|
|
367
436
|
sources,
|
|
368
437
|
src: sources[0],
|
|
369
438
|
loadingState: 'loading',
|
|
370
439
|
error: null,
|
|
440
|
+
started: false,
|
|
371
441
|
})
|
|
372
442
|
|
|
373
443
|
this._events.emit('sourceschange', sources)
|
|
@@ -447,6 +517,11 @@ export class Player implements IPlayer {
|
|
|
447
517
|
})
|
|
448
518
|
}
|
|
449
519
|
|
|
520
|
+
/** The image shown before playback, and on an embed's cover. */
|
|
521
|
+
setPoster(url: string): void {
|
|
522
|
+
this._store.set('poster', url)
|
|
523
|
+
}
|
|
524
|
+
|
|
450
525
|
getSrc(): Src | null {
|
|
451
526
|
return this.state.src
|
|
452
527
|
}
|
package/src/providers/vimeo.ts
CHANGED
|
@@ -274,6 +274,8 @@ export class VimeoProvider extends BaseProvider {
|
|
|
274
274
|
this.iframe = this.container_el.querySelector('iframe')
|
|
275
275
|
if (this.iframe) {
|
|
276
276
|
this.iframe.className = 'ts-video-player__media'
|
|
277
|
+
// Filling the player, whatever size the provider's API gave the frame.
|
|
278
|
+
this.iframe.style.cssText = 'position:absolute;inset:0;width:100%;height:100%;border:0'
|
|
277
279
|
}
|
|
278
280
|
|
|
279
281
|
// Setup event listeners
|
package/src/providers/youtube.ts
CHANGED
|
@@ -254,6 +254,8 @@ export class YouTubeProvider extends BaseProvider {
|
|
|
254
254
|
// Get iframe reference
|
|
255
255
|
this.iframe = this.player.getIframe()
|
|
256
256
|
this.iframe.className = 'ts-video-player__media'
|
|
257
|
+
// Filling the player, whatever size the provider's API gave the frame.
|
|
258
|
+
this.iframe.style.cssText = 'position:absolute;inset:0;width:100%;height:100%;border:0'
|
|
257
259
|
}
|
|
258
260
|
|
|
259
261
|
private async loadYouTubeAPI(): Promise<void> {
|
package/src/types.ts
CHANGED