@vyonio/player 0.0.0-stage → 0.2.0
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 +273 -2
- package/THIRD_PARTY_NOTICES.txt +682 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.js +38 -0
- package/dist/plyr-icons.svg +1 -0
- package/dist/style.css +2 -0
- package/package.json +37 -4
package/README.md
CHANGED
|
@@ -1,3 +1,274 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @vyonio/player
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Shaka Player engine with Plyr chrome for Vyonio DRM video playback. Plyr styles and the control-icon SVG sprite ship with the package, so playback does not depend on the Plyr CDN or require it in the host's Content Security Policy. If Plyr fails to initialize, the player falls back to native `<video controls>`.
|
|
4
|
+
|
|
5
|
+
> **Release availability:** source checkouts can be ahead of npm. Check available
|
|
6
|
+
> versions with `npm view @vyonio/player versions`. For an unpublished checkout,
|
|
7
|
+
> use `npm run verify:package` or a local `npm pack` tarball. Customer setup is in
|
|
8
|
+
> the [public npm guide](https://developers.vyonio.com/npm-player/) and
|
|
9
|
+
> [shared backend guide](https://developers.vyonio.com/protected-playback/).
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @vyonio/player
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The ESM package bundles Shaka and Plyr and exports TypeScript declarations.
|
|
18
|
+
Import `@vyonio/player/style.css` once in your application. The default API is
|
|
19
|
+
`https://api.vyonio.com`; set `apiBase` only when using another environment.
|
|
20
|
+
|
|
21
|
+
For a clean repository checkout, first run `npm ci --prefix packages/player`,
|
|
22
|
+
then `npm ci --prefix <app>` for each app you use (`client`, `course`, `player`,
|
|
23
|
+
`dash`). App build and dev scripts build the shared package before starting;
|
|
24
|
+
they do not install dependencies. Run `npm --prefix packages/player run verify:package` to pack, install, type-check, and build an independent consumer.
|
|
25
|
+
`npm pack` runs the library build through `prepack`. Local verification prepares
|
|
26
|
+
a release artifact; publishing npm and hosted artifacts is a separate step.
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
### Public video
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
import { VyonioPlayer } from '@vyonio/player';
|
|
34
|
+
import '@vyonio/player/style.css';
|
|
35
|
+
|
|
36
|
+
const { player, destroy } = await VyonioPlayer.mount(element, {
|
|
37
|
+
apiBase: 'http://localhost:3000',
|
|
38
|
+
embedUuid: '550e8400-e29b-41d4-a716-446655440000',
|
|
39
|
+
autoplay: false,
|
|
40
|
+
muted: false,
|
|
41
|
+
loop: false,
|
|
42
|
+
controls: true,
|
|
43
|
+
controlItems: ['play-large', 'play', 'progress', 'current-time', 'fullscreen'],
|
|
44
|
+
settingsItems: ['quality'],
|
|
45
|
+
language: 'cs',
|
|
46
|
+
accentColor: '#7c3aed',
|
|
47
|
+
badge: {
|
|
48
|
+
text: 'AI-assisted',
|
|
49
|
+
position: 'top-right',
|
|
50
|
+
style: 'accent'
|
|
51
|
+
},
|
|
52
|
+
startSeconds: 0
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The optional playback settings are intended for hosted embeds as well as
|
|
57
|
+
direct integrations. Browsers generally require `muted: true` when autoplay is
|
|
58
|
+
enabled. A rejected autoplay attempt leaves the player ready for manual play.
|
|
59
|
+
`controlItems` accepts Plyr control names and is ignored when `controls` is
|
|
60
|
+
false. `settingsItems` accepts `quality` and `speed`; it defaults to
|
|
61
|
+
`['quality']`, so playback speed is opt-in. An empty array removes the settings
|
|
62
|
+
menu, and unavailable quality choices hide automatically. `language` accepts
|
|
63
|
+
`en` and `cs` and defaults to English. Unsupported languages fall back to
|
|
64
|
+
English. `accentColor` accepts only a six-digit hex color. Badge text is rendered
|
|
65
|
+
as plain text inside Plyr's fullscreen container and is limited to 64
|
|
66
|
+
characters; positions are the four corners and styles are `dark`, `light`, or
|
|
67
|
+
`accent`.
|
|
68
|
+
|
|
69
|
+
### Popularity histogram
|
|
70
|
+
|
|
71
|
+
Pass equal-duration popularity buckets to render a YouTube-like engagement
|
|
72
|
+
curve above the seek bar (white top stroke, fade-to-transparent fill). The
|
|
73
|
+
overlay is non-interactive and only appears while the progress control is
|
|
74
|
+
hovered or scrubbed:
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
await VyonioPlayer.mount(element, {
|
|
78
|
+
apiBase: 'http://localhost:3000',
|
|
79
|
+
embedUuid: '550e8400-e29b-41d4-a716-446655440000',
|
|
80
|
+
popularityHistogram: {
|
|
81
|
+
buckets: [12, 18, 31, 24, 15]
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Each bucket spans an equal portion of the video duration, and heights are
|
|
87
|
+
normalized against the largest value. The player also accepts the same array
|
|
88
|
+
from `popularity_histogram.buckets` in the playback descriptor when a backend
|
|
89
|
+
provides it. Explicit mount-option buckets take precedence. The current Vyonio
|
|
90
|
+
Platform descriptor does not supply popularity buckets; aggregation and API
|
|
91
|
+
delivery are a separate backend feature.
|
|
92
|
+
|
|
93
|
+
HTML badges stay visible in normal Plyr fullscreen, but cannot be guaranteed in
|
|
94
|
+
native video fallback, Picture in Picture, AirPlay, casting, or downloaded
|
|
95
|
+
media. Use a burned-in watermark for compliance-critical or tamper-resistant
|
|
96
|
+
marking.
|
|
97
|
+
|
|
98
|
+
Fetches the video descriptor from `{apiBase}/v1/embed/{embedUuid}`, attaches a `<video>` element with Plyr controls (or native controls on Plyr failure), configures Widevine DRM, and loads the DASH manifest. Safari/FairPlay is scaffolded and throws until Apple FPS credentials are deployed.
|
|
99
|
+
|
|
100
|
+
The returned handle also exposes media controls used by the hosted iframe API:
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
const handle = await VyonioPlayer.mount(element, options);
|
|
104
|
+
|
|
105
|
+
await handle.play();
|
|
106
|
+
handle.pause();
|
|
107
|
+
handle.seek(30);
|
|
108
|
+
handle.setMuted(true);
|
|
109
|
+
console.log(handle.getState());
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`seek(seconds)` accepts a nonnegative finite number and clamps it to the known
|
|
113
|
+
duration. `getState()` returns `currentTime`, `duration`, `paused`, `ended`, and
|
|
114
|
+
`muted`. The underlying `HTMLVideoElement` is available as `mediaElement` for
|
|
115
|
+
advanced event handling.
|
|
116
|
+
|
|
117
|
+
If the descriptor includes a `telemetry` endpoint and signed context, the
|
|
118
|
+
player automatically emits batched `start`, `heartbeat`, `pause`, `seek`,
|
|
119
|
+
`end`, `leave`, and `error` events. Telemetry is best-effort: failures and an
|
|
120
|
+
absent telemetry descriptor never interrupt playback. Pipeline credentials are
|
|
121
|
+
held by the Platform API and are never exposed to the browser.
|
|
122
|
+
|
|
123
|
+
Every mounted player uses a fresh random session UUID. Heartbeat, pause, end,
|
|
124
|
+
and leave events close continuous watched spans with millisecond start/end,
|
|
125
|
+
duration, watched time, buffering time, and playback rate. A seek closes the
|
|
126
|
+
preceding span and records only its origin and destination, so skipped content
|
|
127
|
+
is not counted as watched. Telemetry contains no user/member ID, stable device
|
|
128
|
+
ID, fingerprint, page/referrer URL, or cross-session cookie.
|
|
129
|
+
|
|
130
|
+
### Protected video (playback JWT)
|
|
131
|
+
|
|
132
|
+
For a same-origin customer endpoint:
|
|
133
|
+
|
|
134
|
+
```js
|
|
135
|
+
const handle = await VyonioPlayer.mount(element, {
|
|
136
|
+
embedUuid: 'your-embed-uuid',
|
|
137
|
+
tokenEndpoint: '/api/playback-token',
|
|
138
|
+
tokenHeaders: { 'X-CSRF-Token': csrfToken }
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
The endpoint receives `POST` JSON `{ embedUuid }` with same-origin credentials
|
|
143
|
+
and must authenticate the viewer and recheck entitlement on every request.
|
|
144
|
+
Return JSON `{ token, expires_in: 600 }` with `Cache-Control: no-store`.
|
|
145
|
+
Keep the Vyonio API key only on your server. Customers never need Vyonio's JWT signing secret.
|
|
146
|
+
`tokenEndpoint` and `getPlaybackToken` are mutually exclusive. Endpoint URLs
|
|
147
|
+
must be same-origin; `tokenHeaders` cannot include Authorization. Token
|
|
148
|
+
acquisition times out after 15 seconds and rejects with `PlaybackTokenError`
|
|
149
|
+
(`PLAYBACK_TOKEN_FAILED` or `PLAYBACK_TOKEN_TIMEOUT`).
|
|
150
|
+
|
|
151
|
+
Pass `tokenEndpoint` or `getPlaybackToken` to attach a short-lived playback JWT before the descriptor fetch. The player:
|
|
152
|
+
|
|
153
|
+
- Sends `Authorization: Bearer <token>` on the descriptor request and all Shaka network requests (via a request filter)
|
|
154
|
+
- Appends `access_token=<token>` to the DASH manifest URL (for AirPlay / devices that cannot send headers)
|
|
155
|
+
- Replaces `access_token` on subsequent platform manifest, segment, and license request URLs after each token refresh
|
|
156
|
+
- Schedules automatic refresh from the JWT `exp` claim, **120 seconds before expiry**
|
|
157
|
+
- Starts refresh scheduling only after the manifest loads successfully
|
|
158
|
+
- On refresh failure, marks the mount and video elements with `data-state="error"`, emits a `vyonio-player-error` event from the mount element, calls optional `onError`, then destroys the player and removes the video element
|
|
159
|
+
|
|
160
|
+
```js
|
|
161
|
+
const { player, destroy } = await VyonioPlayer.mount(element, {
|
|
162
|
+
apiBase: 'http://localhost:3000',
|
|
163
|
+
embedUuid: '550e8400-e29b-41d4-a716-446655440000',
|
|
164
|
+
onError: ({ code, message, error }) => {
|
|
165
|
+
// code is PLAYBACK_AUTH_REFRESH_FAILED for token refresh failures.
|
|
166
|
+
showPlaybackError(message);
|
|
167
|
+
},
|
|
168
|
+
getPlaybackToken: async () => {
|
|
169
|
+
const res = await fetch('/api/playback-token', {
|
|
170
|
+
method: 'POST',
|
|
171
|
+
credentials: 'same-origin',
|
|
172
|
+
cache: 'no-store',
|
|
173
|
+
redirect: 'error',
|
|
174
|
+
headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken },
|
|
175
|
+
body: JSON.stringify({ embedUuid: '550e8400-e29b-41d4-a716-446655440000' })
|
|
176
|
+
});
|
|
177
|
+
if (!res.ok) throw new Error(`Token mint failed: ${res.status}`);
|
|
178
|
+
const { token } = await res.json();
|
|
179
|
+
return token;
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The `vyonio-player-error` event is a `CustomEvent` with the same `{ code, message, error }` object in `event.detail`. This lets applications surface an authentication or entitlement error even when they do not provide `onError`.
|
|
185
|
+
|
|
186
|
+
When both authentication options are omitted, behavior matches the public path (no Bearer header, no manifest query param, no refresh timer).
|
|
187
|
+
|
|
188
|
+
## Playback descriptor: captions and chapters
|
|
189
|
+
|
|
190
|
+
The embed descriptor (`GET {apiBase}/v1/embed/{embedUuid}`) may include:
|
|
191
|
+
|
|
192
|
+
```json
|
|
193
|
+
{
|
|
194
|
+
"captions": [
|
|
195
|
+
{
|
|
196
|
+
"language": "cs",
|
|
197
|
+
"label": "Čeština",
|
|
198
|
+
"kind": "subtitles",
|
|
199
|
+
"vtt_url": "https://media.example/caption_tracks/1/vtt",
|
|
200
|
+
"default": true
|
|
201
|
+
}
|
|
202
|
+
],
|
|
203
|
+
"chapters": [
|
|
204
|
+
{ "title": "Intro", "start_seconds": 0 },
|
|
205
|
+
{ "title": "Main", "start_seconds": 42 }
|
|
206
|
+
]
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
- **`captions`**: optional array of sidecar WebVTT tracks. Each entry supplies `language`, `label`, `kind` (`subtitles` or `captions`), authenticated `vtt_url`, and optional `default` (boolean). When `getPlaybackToken` is used, the player appends `access_token` to each `vtt_url` the same way it does for the DASH manifest.
|
|
211
|
+
- **`chapters`**: optional array of chapter cues with `title` and `start_seconds`. The player builds an in-memory chapters WebVTT track for Plyr's chapter menu; cue end times are the next chapter start or video duration.
|
|
212
|
+
|
|
213
|
+
When either field is absent or empty, the corresponding Plyr control stays idle.
|
|
214
|
+
|
|
215
|
+
## Tests
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
npm test
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## React and SSR
|
|
222
|
+
|
|
223
|
+
The browser engine uses DOM globals. Import `@vyonio/player` dynamically inside
|
|
224
|
+
a client effect; do not import the engine in a server-evaluated module. Import
|
|
225
|
+
the CSS once through your framework's supported global CSS entry. Cancel stale
|
|
226
|
+
mounts, destroy their late handles, and call `destroy()` on cleanup. The
|
|
227
|
+
[public npm guide](https://developers.vyonio.com/npm-player/#react-and-server-rendering)
|
|
228
|
+
contains the complete lifecycle example.
|
|
229
|
+
|
|
230
|
+
See the [runnable customer example](../../examples/protected-playback/README.md)
|
|
231
|
+
for the same-origin `{ embedUuid }` request, CSRF `tokenHeaders`, server-only key,
|
|
232
|
+
current entitlement checks, and Node/PHP host replacement points. Its login is
|
|
233
|
+
local demonstration authentication, not a production identity system. Protected
|
|
234
|
+
access can be used with clear or DRM media; this package preserves your existing
|
|
235
|
+
account DRM configuration and does not provision FairPlay.
|
|
236
|
+
|
|
237
|
+
## Maintainer releases
|
|
238
|
+
|
|
239
|
+
From the repository root, use Node 22.12+ and your own npm account with Vyonio
|
|
240
|
+
publish access. Commit package changes first, leave the Git index empty, and sign
|
|
241
|
+
in once with `npm login`. Use a named branch with Git `user.name`/`user.email`
|
|
242
|
+
configured and no merge/rebase in progress.
|
|
243
|
+
|
|
244
|
+
```sh
|
|
245
|
+
# First publication of the existing package version:
|
|
246
|
+
npm --prefix packages/player run release -- current --dry-run
|
|
247
|
+
npm --prefix packages/player run release -- current
|
|
248
|
+
|
|
249
|
+
# Later bug-fix release:
|
|
250
|
+
npm --prefix packages/player run release -- patch --dry-run
|
|
251
|
+
npm --prefix packages/player run release -- patch
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Choose `minor` for a feature release or `major` for breaking changes. These commands
|
|
255
|
+
accept stable versions only; prereleases need a separately reviewed release.
|
|
256
|
+
The command builds a temporary copy of tracked package source, installs locked
|
|
257
|
+
dependencies, runs all package tests, and verifies the tarball in an independent
|
|
258
|
+
TypeScript/browser consumer. A real release asks for confirmation and uses npm's
|
|
259
|
+
normal login/2FA flow. Only that verified tarball is published. It then checks
|
|
260
|
+
registry integrity and installs the exact version from npm.
|
|
261
|
+
|
|
262
|
+
Dry runs, cancellation and failed preparation leave your working version files
|
|
263
|
+
unchanged, and create no commits or tags. After publication and registry
|
|
264
|
+
installation verification, a bumped release commits only the manifest and
|
|
265
|
+
lockfile, then creates an annotated `player-vX.Y.Z` tag on that exact commit.
|
|
266
|
+
`current` tags the existing matching-version commit without adding an empty
|
|
267
|
+
commit. The annotation records the npm artifact integrity. Existing local/origin
|
|
268
|
+
tags are never overwritten. Unrelated unstaged work stays outside the release
|
|
269
|
+
commit; any staged work blocks release.
|
|
270
|
+
|
|
271
|
+
Review the local release commit and tag, then push them through your normal Git
|
|
272
|
+
workflow. The command does not push tags or deploy hosted/docs assets. Read the
|
|
273
|
+
[release runbook](../../docs/player-release.md) for first-release setup, Git/tag
|
|
274
|
+
recovery and post-release documentation.
|