@guardvideo/player-sdk 1.1.0 → 1.3.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 +292 -241
- package/dist/index.esm.js +760 -632
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +759 -631
- package/dist/index.js.map +1 -1
- package/dist/react/GuardVideoPlayer.d.ts +8 -5
- package/dist/react/GuardVideoPlayer.d.ts.map +1 -1
- package/dist/ui/PlayerUI.d.ts +94 -0
- package/dist/ui/PlayerUI.d.ts.map +1 -0
- package/dist/vanilla/guardvideo-player.js +869 -23
- package/dist/vanilla/guardvideo-player.js.map +1 -1
- package/dist/vanilla/guardvideo-player.min.js +1 -37321
- package/dist/vanilla/guardvideo-player.min.js.map +1 -1
- package/dist/vanilla/react/GuardVideoPlayer.d.ts +8 -5
- package/dist/vanilla/react/GuardVideoPlayer.d.ts.map +1 -1
- package/dist/vanilla/ui/PlayerUI.d.ts +94 -0
- package/dist/vanilla/ui/PlayerUI.d.ts.map +1 -0
- package/dist/vanilla/vanilla/index.d.ts +24 -7
- package/dist/vanilla/vanilla/index.d.ts.map +1 -1
- package/package.json +2 -1
|
@@ -37281,33 +37281,878 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37281
37281
|
}
|
|
37282
37282
|
};
|
|
37283
37283
|
|
|
37284
|
-
|
|
37285
|
-
|
|
37286
|
-
|
|
37287
|
-
|
|
37288
|
-
|
|
37289
|
-
|
|
37290
|
-
|
|
37291
|
-
|
|
37292
|
-
|
|
37293
|
-
|
|
37294
|
-
|
|
37295
|
-
|
|
37284
|
+
function formatTime(s) {
|
|
37285
|
+
if (!isFinite(s) || isNaN(s))
|
|
37286
|
+
return '0:00';
|
|
37287
|
+
const h = Math.floor(s / 3600);
|
|
37288
|
+
const m = Math.floor((s % 3600) / 60);
|
|
37289
|
+
const sc = Math.floor(s % 60);
|
|
37290
|
+
const mm = String(m).padStart(h > 0 ? 2 : 1, '0');
|
|
37291
|
+
const ss = String(sc).padStart(2, '0');
|
|
37292
|
+
return h > 0 ? `${h}:${mm}:${ss}` : `${mm}:${ss}`;
|
|
37293
|
+
}
|
|
37294
|
+
function el(tag, cls, attrs) {
|
|
37295
|
+
const e = document.createElement(tag);
|
|
37296
|
+
if (cls)
|
|
37297
|
+
e.className = cls;
|
|
37298
|
+
if (attrs)
|
|
37299
|
+
for (const [k, v] of Object.entries(attrs))
|
|
37300
|
+
e.setAttribute(k, v);
|
|
37301
|
+
return e;
|
|
37302
|
+
}
|
|
37303
|
+
function svgEl(path, w = 20, h = 20) {
|
|
37304
|
+
const s = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
37305
|
+
s.setAttribute('width', String(w));
|
|
37306
|
+
s.setAttribute('height', String(h));
|
|
37307
|
+
s.setAttribute('viewBox', '0 0 24 24');
|
|
37308
|
+
s.setAttribute('fill', 'currentColor');
|
|
37309
|
+
s.setAttribute('aria-hidden', 'true');
|
|
37310
|
+
s.innerHTML = path;
|
|
37311
|
+
return s;
|
|
37312
|
+
}
|
|
37313
|
+
const ICON = {
|
|
37314
|
+
play: '<polygon points="5,3 19,12 5,21"/>',
|
|
37315
|
+
pause: '<rect x="6" y="4" width="4" height="16" rx="1"/><rect x="14" y="4" width="4" height="16" rx="1"/>',
|
|
37316
|
+
replay: '<path d="M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"/>',
|
|
37317
|
+
volHigh: '<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/>',
|
|
37318
|
+
volMute: '<path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"/>',
|
|
37319
|
+
fsEnter: '<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/>',
|
|
37320
|
+
fsExit: '<path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"/>',
|
|
37321
|
+
settings: '<path d="M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94zM12,15.6c-1.98,0-3.6-1.62-3.6-3.6s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z"/>',
|
|
37322
|
+
shield: '<path fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>',
|
|
37323
|
+
};
|
|
37324
|
+
const SPEEDS = [0.5, 0.75, 1, 1.25, 1.5, 2];
|
|
37325
|
+
let _stylesInjected = false;
|
|
37326
|
+
function injectStyles() {
|
|
37327
|
+
if (_stylesInjected)
|
|
37328
|
+
return;
|
|
37329
|
+
_stylesInjected = true;
|
|
37330
|
+
const css = `
|
|
37331
|
+
/* ── GuardVideo Player UI ─────────────────────────────────── */
|
|
37332
|
+
.gvp-root {
|
|
37333
|
+
--gvp-accent: #44c09b;
|
|
37334
|
+
position: relative;
|
|
37335
|
+
background: #000;
|
|
37336
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
37337
|
+
border-radius: 10px;
|
|
37338
|
+
overflow: hidden;
|
|
37339
|
+
-webkit-user-select: none;
|
|
37340
|
+
user-select: none;
|
|
37341
|
+
outline: none;
|
|
37342
|
+
}
|
|
37343
|
+
.gvp-root:focus-visible { outline: 2px solid var(--gvp-accent); outline-offset: 1px; }
|
|
37344
|
+
|
|
37345
|
+
.gvp-video { display: block; width: 100%; height: 100%; object-fit: contain; }
|
|
37346
|
+
|
|
37347
|
+
/* ── Utility: hide elements ────────────────────────────────── */
|
|
37348
|
+
.gvp-hidden { display: none !important; }
|
|
37349
|
+
/* Controls use opacity/translate so they animate out gracefully */
|
|
37350
|
+
.gvp-controls-hidden { opacity: 0; transform: translateY(6px); pointer-events: none; }
|
|
37351
|
+
|
|
37352
|
+
/* ── Spinner ─────────────────────────────────────────────── */
|
|
37353
|
+
.gvp-spinner {
|
|
37354
|
+
position: absolute; inset: 0; display: flex;
|
|
37355
|
+
align-items: center; justify-content: center;
|
|
37356
|
+
pointer-events: none; background: rgba(0,0,0,.55); border-radius: 10px;
|
|
37357
|
+
}
|
|
37358
|
+
.gvp-spinner-ring {
|
|
37359
|
+
width: 48px; height: 48px;
|
|
37360
|
+
border: 3px solid rgba(255,255,255,.15);
|
|
37361
|
+
border-top-color: var(--gvp-accent);
|
|
37362
|
+
border-radius: 50%; animation: gvp-spin .8s linear infinite;
|
|
37363
|
+
}
|
|
37364
|
+
@keyframes gvp-spin { to { transform: rotate(360deg); } }
|
|
37365
|
+
|
|
37366
|
+
/* ── Centre play button ────────────────────────────────────── */
|
|
37367
|
+
.gvp-center-play {
|
|
37368
|
+
position: absolute; inset: 0; z-index: 3;
|
|
37369
|
+
display: flex; align-items: center; justify-content: center;
|
|
37370
|
+
cursor: pointer; background: rgba(0,0,0,.3);
|
|
37371
|
+
transition: background .2s; border-radius: 10px;
|
|
37372
|
+
}
|
|
37373
|
+
.gvp-center-play:hover { background: rgba(0,0,0,.45); }
|
|
37374
|
+
.gvp-center-play-btn {
|
|
37375
|
+
width: 72px; height: 72px;
|
|
37376
|
+
background: rgba(255,255,255,.12); backdrop-filter: blur(8px);
|
|
37377
|
+
border-radius: 50%; display: flex; align-items: center; justify-content: center;
|
|
37378
|
+
border: 2px solid rgba(255,255,255,.25);
|
|
37379
|
+
transition: background .2s, transform .15s; color: #fff;
|
|
37380
|
+
}
|
|
37381
|
+
.gvp-center-play:hover .gvp-center-play-btn { background: var(--gvp-accent); transform: scale(1.08); }
|
|
37382
|
+
|
|
37383
|
+
/* ── Click-to-toggle overlay ───────────────────────────────── */
|
|
37384
|
+
.gvp-click-area { position: absolute; inset: 0; cursor: pointer; z-index: 1; }
|
|
37385
|
+
|
|
37386
|
+
/* ── Ripple ───────────────────────────────────────────────── */
|
|
37387
|
+
.gvp-ripple {
|
|
37388
|
+
position: absolute; border-radius: 50%; transform: scale(0);
|
|
37389
|
+
background: rgba(255,255,255,.25);
|
|
37390
|
+
animation: gvp-ripple-anim .5s ease-out forwards;
|
|
37391
|
+
pointer-events: none; z-index: 2;
|
|
37392
|
+
}
|
|
37393
|
+
@keyframes gvp-ripple-anim { to { transform: scale(4); opacity: 0; } }
|
|
37394
|
+
|
|
37395
|
+
/* ── Controls bar ──────────────────────────────────────────── */
|
|
37396
|
+
.gvp-controls {
|
|
37397
|
+
position: absolute; bottom: 0; left: 0; right: 0; z-index: 10;
|
|
37398
|
+
background: linear-gradient(to top, rgba(0,0,0,.85) 0%, transparent 100%);
|
|
37399
|
+
padding: 36px 14px 10px;
|
|
37400
|
+
transition: opacity .3s, transform .3s;
|
|
37401
|
+
border-radius: 0 0 10px 10px;
|
|
37402
|
+
}
|
|
37403
|
+
|
|
37404
|
+
/* ── Seek bar ──────────────────────────────────────────────── */
|
|
37405
|
+
.gvp-seek-row { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
|
|
37406
|
+
.gvp-seek-wrap {
|
|
37407
|
+
flex: 1; position: relative; height: 4px; cursor: pointer;
|
|
37408
|
+
padding: 8px 0; margin: -8px 0; box-sizing: content-box;
|
|
37409
|
+
/* Prevent page scroll while touch-seeking */
|
|
37410
|
+
touch-action: none;
|
|
37411
|
+
}
|
|
37412
|
+
.gvp-seek-track {
|
|
37413
|
+
height: 4px; background: rgba(255,255,255,.2); border-radius: 4px;
|
|
37414
|
+
position: relative; overflow: hidden; transition: height .15s;
|
|
37415
|
+
pointer-events: none;
|
|
37416
|
+
}
|
|
37417
|
+
.gvp-seek-wrap:hover .gvp-seek-track,
|
|
37418
|
+
.gvp-seek-wrap.gvp-dragging .gvp-seek-track { height: 6px; }
|
|
37419
|
+
|
|
37420
|
+
.gvp-seek-buffered {
|
|
37421
|
+
position: absolute; left: 0; top: 0; height: 100%;
|
|
37422
|
+
background: rgba(255,255,255,.35); border-radius: 4px; pointer-events: none;
|
|
37423
|
+
}
|
|
37424
|
+
.gvp-seek-progress {
|
|
37425
|
+
position: absolute; left: 0; top: 0; height: 100%;
|
|
37426
|
+
background: var(--gvp-accent); border-radius: 4px; pointer-events: none;
|
|
37427
|
+
}
|
|
37428
|
+
.gvp-seek-thumb {
|
|
37429
|
+
position: absolute; top: 50%; transform: translate(-50%,-50%);
|
|
37430
|
+
width: 13px; height: 13px; background: #fff; border-radius: 50%;
|
|
37431
|
+
box-shadow: 0 1px 4px rgba(0,0,0,.5); pointer-events: none;
|
|
37432
|
+
opacity: 0; transition: opacity .15s;
|
|
37433
|
+
}
|
|
37434
|
+
.gvp-seek-wrap:hover .gvp-seek-thumb,
|
|
37435
|
+
.gvp-seek-wrap.gvp-dragging .gvp-seek-thumb { opacity: 1; }
|
|
37436
|
+
|
|
37437
|
+
.gvp-seek-tooltip {
|
|
37438
|
+
position: absolute; bottom: 24px; transform: translateX(-50%);
|
|
37439
|
+
background: rgba(0,0,0,.8); color: #fff; font-size: 11px; font-weight: 500;
|
|
37440
|
+
padding: 3px 7px; border-radius: 4px;
|
|
37441
|
+
pointer-events: none; white-space: nowrap;
|
|
37442
|
+
opacity: 0; transition: opacity .1s;
|
|
37443
|
+
}
|
|
37444
|
+
.gvp-seek-wrap:hover .gvp-seek-tooltip { opacity: 1; }
|
|
37445
|
+
|
|
37446
|
+
/* ── Button row ────────────────────────────────────────────── */
|
|
37447
|
+
.gvp-btn-row { display: flex; align-items: center; gap: 4px; }
|
|
37448
|
+
.gvp-btn {
|
|
37449
|
+
background: none; border: none; color: #fff; cursor: pointer;
|
|
37450
|
+
padding: 5px; border-radius: 6px;
|
|
37451
|
+
display: flex; align-items: center; justify-content: center;
|
|
37452
|
+
transition: background .15s, color .15s; flex-shrink: 0;
|
|
37453
|
+
}
|
|
37454
|
+
.gvp-btn:hover { background: rgba(255,255,255,.12); color: var(--gvp-accent); }
|
|
37455
|
+
.gvp-btn:active { background: rgba(255,255,255,.2); }
|
|
37456
|
+
|
|
37457
|
+
/* ── Volume ────────────────────────────────────────────────── */
|
|
37458
|
+
.gvp-volume-wrap { display: flex; align-items: center; gap: 5px; }
|
|
37459
|
+
.gvp-volume-slider {
|
|
37460
|
+
-webkit-appearance: none; width: 70px; height: 4px;
|
|
37461
|
+
background: rgba(255,255,255,.25); border-radius: 4px;
|
|
37462
|
+
outline: none; cursor: pointer; accent-color: var(--gvp-accent);
|
|
37463
|
+
}
|
|
37464
|
+
.gvp-volume-slider::-webkit-slider-thumb {
|
|
37465
|
+
-webkit-appearance: none; width: 12px; height: 12px;
|
|
37466
|
+
border-radius: 50%; background: #fff; cursor: pointer;
|
|
37467
|
+
box-shadow: 0 1px 3px rgba(0,0,0,.4);
|
|
37468
|
+
}
|
|
37469
|
+
.gvp-volume-slider::-moz-range-thumb {
|
|
37470
|
+
width: 12px; height: 12px; border-radius: 50%;
|
|
37471
|
+
background: #fff; cursor: pointer; border: none;
|
|
37472
|
+
}
|
|
37473
|
+
|
|
37474
|
+
/* ── Time display ──────────────────────────────────────────── */
|
|
37475
|
+
.gvp-time {
|
|
37476
|
+
font-size: 12px; color: rgba(255,255,255,.85);
|
|
37477
|
+
font-variant-numeric: tabular-nums; white-space: nowrap; letter-spacing: .02em;
|
|
37478
|
+
}
|
|
37479
|
+
.gvp-spacer { flex: 1; }
|
|
37480
|
+
|
|
37481
|
+
/* ── Popup menus ───────────────────────────────────────────── */
|
|
37482
|
+
.gvp-menu-wrap { position: relative; }
|
|
37483
|
+
.gvp-menu {
|
|
37484
|
+
position: absolute; bottom: calc(100% + 8px); right: 0;
|
|
37485
|
+
background: rgba(18,18,22,.95);
|
|
37486
|
+
backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
|
|
37487
|
+
border: 1px solid rgba(255,255,255,.08); border-radius: 8px;
|
|
37488
|
+
min-width: 140px; overflow: hidden;
|
|
37489
|
+
box-shadow: 0 8px 24px rgba(0,0,0,.5);
|
|
37490
|
+
animation: gvp-menu-in .1s ease-out; z-index: 20;
|
|
37491
|
+
}
|
|
37492
|
+
@keyframes gvp-menu-in {
|
|
37493
|
+
from { opacity: 0; transform: scale(.95) translateY(4px); }
|
|
37494
|
+
to { opacity: 1; transform: none; }
|
|
37495
|
+
}
|
|
37496
|
+
.gvp-menu-title {
|
|
37497
|
+
font-size: 10px; font-weight: 600; text-transform: uppercase;
|
|
37498
|
+
letter-spacing: .08em; color: rgba(255,255,255,.4); padding: 8px 12px 4px;
|
|
37499
|
+
}
|
|
37500
|
+
.gvp-menu-item {
|
|
37501
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
37502
|
+
padding: 7px 12px; font-size: 13px; color: rgba(255,255,255,.85);
|
|
37503
|
+
cursor: pointer; transition: background .12s; gap: 10px;
|
|
37504
|
+
}
|
|
37505
|
+
.gvp-menu-item:hover { background: rgba(255,255,255,.07); }
|
|
37506
|
+
.gvp-menu-item-active { color: var(--gvp-accent); font-weight: 600; }
|
|
37507
|
+
.gvp-menu-check { font-size: 14px; color: var(--gvp-accent); }
|
|
37508
|
+
.gvp-menu-sep { height: 1px; background: rgba(255,255,255,.07); margin: 3px 0; }
|
|
37509
|
+
|
|
37510
|
+
/* ── Error overlay ─────────────────────────────────────────── */
|
|
37511
|
+
.gvp-error {
|
|
37512
|
+
position: absolute; inset: 0; z-index: 15;
|
|
37513
|
+
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
37514
|
+
background: rgba(0,0,0,.8); color: #fff; gap: 10px; padding: 24px;
|
|
37515
|
+
border-radius: 10px; text-align: center;
|
|
37516
|
+
}
|
|
37517
|
+
.gvp-error-icon { font-size: 36px; }
|
|
37518
|
+
.gvp-error-code { font-size: 11px; font-weight: 700; letter-spacing: .1em; color: #f87171; text-transform: uppercase; }
|
|
37519
|
+
.gvp-error-msg { font-size: 14px; color: rgba(255,255,255,.7); max-width: 320px; line-height: 1.5; }
|
|
37520
|
+
|
|
37521
|
+
/* ── Secure badge ──────────────────────────────────────────── */
|
|
37522
|
+
.gvp-badge {
|
|
37523
|
+
position: absolute; top: 10px; right: 12px; z-index: 5;
|
|
37524
|
+
display: flex; align-items: center; gap: 5px;
|
|
37525
|
+
background: rgba(0,0,0,.55); backdrop-filter: blur(6px);
|
|
37526
|
+
border-radius: 20px; padding: 3px 9px 3px 7px;
|
|
37527
|
+
font-size: 10px; font-weight: 600; color: var(--gvp-accent);
|
|
37528
|
+
pointer-events: none; letter-spacing: .04em; transition: opacity .3s;
|
|
37529
|
+
}
|
|
37530
|
+
.gvp-badge-hidden { opacity: 0; }
|
|
37531
|
+
|
|
37532
|
+
/* ── Forensic watermark ────────────────────────────────────── */
|
|
37533
|
+
.gvp-watermark { position: absolute; inset: 0; pointer-events: none; overflow: hidden; z-index: 6; }
|
|
37534
|
+
.gvp-watermark-text {
|
|
37535
|
+
position: absolute; white-space: nowrap; font-size: 13px; font-family: monospace;
|
|
37536
|
+
color: rgba(255,255,255,.065); transform: rotate(-28deg);
|
|
37537
|
+
user-select: none; pointer-events: none; letter-spacing: .05em;
|
|
37538
|
+
}
|
|
37539
|
+
|
|
37540
|
+
/* ── Speed label button ────────────────────────────────────── */
|
|
37541
|
+
.gvp-rate-label {
|
|
37542
|
+
font-size: 11px; font-weight: 700; color: rgba(255,255,255,.7);
|
|
37543
|
+
min-width: 28px; text-align: center; cursor: pointer;
|
|
37544
|
+
padding: 5px 4px; border-radius: 4px; transition: background .12s; flex-shrink: 0;
|
|
37545
|
+
}
|
|
37546
|
+
.gvp-rate-label:hover { background: rgba(255,255,255,.1); }
|
|
37547
|
+
`;
|
|
37548
|
+
const tag = document.createElement('style');
|
|
37549
|
+
tag.setAttribute('data-guardvideo', 'player-ui-styles');
|
|
37550
|
+
tag.textContent = css;
|
|
37551
|
+
document.head.appendChild(tag);
|
|
37552
|
+
}
|
|
37553
|
+
class PlayerUI {
|
|
37554
|
+
constructor(container, videoId, config) {
|
|
37555
|
+
this.playerState = exports.PlayerState.IDLE;
|
|
37556
|
+
this.duration = 0;
|
|
37557
|
+
this.bufferedEnd = 0;
|
|
37558
|
+
this.qualityLevels = [];
|
|
37559
|
+
this.currentQualityIdx = -1;
|
|
37560
|
+
this.playbackRate = 1;
|
|
37561
|
+
this.openMenu = null;
|
|
37562
|
+
this.hideTimer = null;
|
|
37563
|
+
this.seekDragging = false;
|
|
37564
|
+
const accent = config.branding?.accentColor ?? '#44c09b';
|
|
37565
|
+
const brandName = config.branding?.name ?? 'GuardVideo';
|
|
37566
|
+
injectStyles();
|
|
37296
37567
|
container.innerHTML = '';
|
|
37297
|
-
|
|
37298
|
-
|
|
37299
|
-
|
|
37300
|
-
|
|
37568
|
+
this.root = el('div', 'gvp-root');
|
|
37569
|
+
this.root.style.width = config.width ?? '100%';
|
|
37570
|
+
this.root.style.height = config.height ?? 'auto';
|
|
37571
|
+
this.root.style.setProperty('--gvp-accent', accent);
|
|
37572
|
+
this.root.setAttribute('tabindex', '0');
|
|
37573
|
+
this.root.setAttribute('role', 'region');
|
|
37574
|
+
this.root.setAttribute('aria-label', `${brandName} video player`);
|
|
37575
|
+
this.videoEl = el('video', 'gvp-video', { playsinline: '', preload: 'metadata' });
|
|
37576
|
+
this.badge = el('div', 'gvp-badge');
|
|
37577
|
+
this.badge.setAttribute('aria-hidden', 'true');
|
|
37578
|
+
this.badge.appendChild(svgEl(ICON.shield, 14, 14));
|
|
37579
|
+
this.badge.appendChild(document.createTextNode(brandName));
|
|
37580
|
+
this.watermarkDiv = el('div', 'gvp-watermark');
|
|
37581
|
+
this.watermarkDiv.setAttribute('aria-hidden', 'true');
|
|
37582
|
+
this.spinner = el('div', 'gvp-spinner gvp-hidden');
|
|
37583
|
+
this.spinner.setAttribute('aria-label', 'Loading');
|
|
37584
|
+
this.spinner.setAttribute('role', 'status');
|
|
37585
|
+
this.spinner.appendChild(el('div', 'gvp-spinner-ring'));
|
|
37586
|
+
this.errorOverlay = el('div', 'gvp-error gvp-hidden');
|
|
37587
|
+
this.errorOverlay.setAttribute('role', 'alert');
|
|
37588
|
+
const errIcon = el('div', 'gvp-error-icon');
|
|
37589
|
+
errIcon.textContent = '⚠️';
|
|
37590
|
+
errIcon.setAttribute('aria-hidden', 'true');
|
|
37591
|
+
const errCode = el('div', 'gvp-error-code');
|
|
37592
|
+
const errMsg = el('div', 'gvp-error-msg');
|
|
37593
|
+
this.errorOverlay.append(errIcon, errCode, errMsg);
|
|
37594
|
+
this.centerPlay = el('div', 'gvp-center-play');
|
|
37595
|
+
this.centerPlay.setAttribute('role', 'button');
|
|
37596
|
+
this.centerPlay.setAttribute('aria-label', 'Play');
|
|
37597
|
+
this.centerPlayBtn = el('div', 'gvp-center-play-btn');
|
|
37598
|
+
this.centerPlayBtn.appendChild(svgEl(ICON.play, 32, 32));
|
|
37599
|
+
this.centerPlay.appendChild(this.centerPlayBtn);
|
|
37600
|
+
this.clickArea = el('div', 'gvp-click-area gvp-hidden');
|
|
37601
|
+
this.clickArea.setAttribute('aria-hidden', 'true');
|
|
37602
|
+
this.controls = el('div', 'gvp-controls');
|
|
37603
|
+
const seekRow = el('div', 'gvp-seek-row');
|
|
37604
|
+
this.seekWrap = el('div', 'gvp-seek-wrap');
|
|
37605
|
+
this.seekWrap.setAttribute('role', 'slider');
|
|
37606
|
+
this.seekWrap.setAttribute('aria-label', 'Seek');
|
|
37607
|
+
this.seekWrap.setAttribute('aria-valuemin', '0');
|
|
37608
|
+
this.seekWrap.setAttribute('aria-valuemax', '100');
|
|
37609
|
+
this.seekWrap.setAttribute('aria-valuenow', '0');
|
|
37610
|
+
this.seekWrap.setAttribute('aria-valuetext', '0:00 of 0:00');
|
|
37611
|
+
this.seekWrap.setAttribute('tabindex', '0');
|
|
37612
|
+
const seekTrack = el('div', 'gvp-seek-track');
|
|
37613
|
+
this.seekBuffered = el('div', 'gvp-seek-buffered');
|
|
37614
|
+
this.seekProgress = el('div', 'gvp-seek-progress');
|
|
37615
|
+
this.seekThumb = el('div', 'gvp-seek-thumb');
|
|
37616
|
+
this.seekTooltip = el('div', 'gvp-seek-tooltip');
|
|
37617
|
+
this.seekTooltip.setAttribute('aria-hidden', 'true');
|
|
37618
|
+
seekTrack.append(this.seekBuffered, this.seekProgress);
|
|
37619
|
+
this.seekWrap.append(seekTrack, this.seekThumb, this.seekTooltip);
|
|
37620
|
+
seekRow.appendChild(this.seekWrap);
|
|
37621
|
+
this.controls.appendChild(seekRow);
|
|
37622
|
+
const btnRow = el('div', 'gvp-btn-row');
|
|
37623
|
+
this.playBtn = el('button', 'gvp-btn');
|
|
37624
|
+
this.playBtn.type = 'button';
|
|
37625
|
+
this.playBtn.setAttribute('aria-label', 'Play');
|
|
37626
|
+
const volWrap = el('div', 'gvp-volume-wrap');
|
|
37627
|
+
this.volBtn = el('button', 'gvp-btn');
|
|
37628
|
+
this.volBtn.type = 'button';
|
|
37629
|
+
this.volBtn.setAttribute('aria-label', 'Mute');
|
|
37630
|
+
this.volBtn.appendChild(svgEl(ICON.volHigh, 18, 18));
|
|
37631
|
+
this.volSlider = el('input', 'gvp-volume-slider');
|
|
37632
|
+
Object.assign(this.volSlider, { type: 'range', min: '0', max: '1', step: '0.02', value: '1' });
|
|
37633
|
+
this.volSlider.setAttribute('aria-label', 'Volume');
|
|
37634
|
+
volWrap.append(this.volBtn, this.volSlider);
|
|
37635
|
+
this.timeEl = el('span', 'gvp-time');
|
|
37636
|
+
this.timeEl.setAttribute('aria-live', 'off');
|
|
37637
|
+
this.timeEl.textContent = '0:00 / 0:00';
|
|
37638
|
+
const spacer = el('div', 'gvp-spacer');
|
|
37639
|
+
const speedWrap = el('div', 'gvp-menu-wrap');
|
|
37640
|
+
this.speedLabel = el('span', 'gvp-rate-label');
|
|
37641
|
+
this.speedLabel.textContent = '1×';
|
|
37642
|
+
this.speedLabel.setAttribute('role', 'button');
|
|
37643
|
+
this.speedLabel.setAttribute('aria-label', 'Playback speed');
|
|
37644
|
+
this.speedLabel.setAttribute('aria-haspopup', 'menu');
|
|
37645
|
+
this.speedMenu = el('div', 'gvp-menu gvp-hidden');
|
|
37646
|
+
this.speedMenu.setAttribute('role', 'menu');
|
|
37647
|
+
this.speedMenu.setAttribute('aria-label', 'Playback speed');
|
|
37648
|
+
const speedTitle = el('div', 'gvp-menu-title');
|
|
37649
|
+
speedTitle.setAttribute('aria-hidden', 'true');
|
|
37650
|
+
speedTitle.textContent = 'Speed';
|
|
37651
|
+
this.speedMenu.appendChild(speedTitle);
|
|
37652
|
+
SPEEDS.forEach(r => {
|
|
37653
|
+
const item = el('div', r === 1 ? 'gvp-menu-item gvp-menu-item-active' : 'gvp-menu-item');
|
|
37654
|
+
item.setAttribute('role', 'menuitemradio');
|
|
37655
|
+
item.setAttribute('aria-checked', r === 1 ? 'true' : 'false');
|
|
37656
|
+
item.dataset['speed'] = String(r);
|
|
37657
|
+
item.textContent = r === 1 ? 'Normal' : `${r}×`;
|
|
37658
|
+
if (r === 1) {
|
|
37659
|
+
const check = el('span', 'gvp-menu-check');
|
|
37660
|
+
check.setAttribute('aria-hidden', 'true');
|
|
37661
|
+
check.textContent = '✓';
|
|
37662
|
+
item.appendChild(check);
|
|
37663
|
+
}
|
|
37664
|
+
this.speedMenu.appendChild(item);
|
|
37665
|
+
});
|
|
37666
|
+
speedWrap.append(this.speedLabel, this.speedMenu);
|
|
37667
|
+
const qualWrap = el('div', 'gvp-menu-wrap');
|
|
37668
|
+
this.settingsBtn = el('button', 'gvp-btn');
|
|
37669
|
+
this.settingsBtn.type = 'button';
|
|
37670
|
+
this.settingsBtn.setAttribute('aria-label', 'Quality settings');
|
|
37671
|
+
this.settingsBtn.setAttribute('aria-haspopup', 'menu');
|
|
37672
|
+
this.settingsBtn.appendChild(svgEl(ICON.settings, 18, 18));
|
|
37673
|
+
this.settingsBtn.classList.add('gvp-hidden');
|
|
37674
|
+
this.qualityMenu = el('div', 'gvp-menu gvp-hidden');
|
|
37675
|
+
this.qualityMenu.setAttribute('role', 'menu');
|
|
37676
|
+
this.qualityMenu.setAttribute('aria-label', 'Video quality');
|
|
37677
|
+
qualWrap.append(this.settingsBtn, this.qualityMenu);
|
|
37678
|
+
this.fsBtn = el('button', 'gvp-btn');
|
|
37679
|
+
this.fsBtn.type = 'button';
|
|
37680
|
+
this.fsBtn.setAttribute('aria-label', 'Enter fullscreen');
|
|
37681
|
+
this.fsBtn.appendChild(svgEl(ICON.fsEnter, 18, 18));
|
|
37682
|
+
this.playBtn.appendChild(svgEl(ICON.play));
|
|
37683
|
+
btnRow.append(this.playBtn, volWrap, this.timeEl, spacer, speedWrap, qualWrap, this.fsBtn);
|
|
37684
|
+
this.controls.appendChild(btnRow);
|
|
37685
|
+
this.root.append(this.videoEl, this.badge, this.watermarkDiv, this.spinner, this.errorOverlay, this.centerPlay, this.clickArea, this.controls);
|
|
37686
|
+
container.appendChild(this.root);
|
|
37687
|
+
this._onFsChangeBound = () => this._onFsChange();
|
|
37688
|
+
this._seekMouseMoveBound = (e) => { if (this.seekDragging)
|
|
37689
|
+
this._seekTo(e.clientX); };
|
|
37690
|
+
this._seekMouseUpBound = () => this._endSeekDrag();
|
|
37691
|
+
this._seekTouchMoveBound = (e) => {
|
|
37692
|
+
if (!this.seekDragging)
|
|
37693
|
+
return;
|
|
37694
|
+
e.preventDefault();
|
|
37695
|
+
this._seekTo(e.touches[0].clientX);
|
|
37696
|
+
};
|
|
37697
|
+
this._seekTouchEndBound = () => this._endSeekDrag();
|
|
37698
|
+
this._wireEvents(videoId, config);
|
|
37699
|
+
if (config.forensicWatermark !== false) {
|
|
37700
|
+
const wmText = config.viewerEmail || config.viewerName || '';
|
|
37701
|
+
if (wmText)
|
|
37702
|
+
this._renderWatermark(wmText);
|
|
37703
|
+
}
|
|
37704
|
+
}
|
|
37705
|
+
_wireEvents(videoId, config) {
|
|
37706
|
+
const video = this.videoEl;
|
|
37707
|
+
this.corePlayer = new GuardVideoPlayer$1(video, videoId, {
|
|
37708
|
+
...config,
|
|
37709
|
+
controls: false,
|
|
37710
|
+
forensicWatermark: config.forensicWatermark !== false,
|
|
37711
|
+
onReady: () => {
|
|
37712
|
+
config.onReady?.();
|
|
37713
|
+
const levels = this.corePlayer.getQualityLevels();
|
|
37714
|
+
if (levels.length) {
|
|
37715
|
+
this.qualityLevels = levels;
|
|
37716
|
+
this._buildQualityMenu();
|
|
37717
|
+
}
|
|
37718
|
+
else {
|
|
37719
|
+
setTimeout(() => {
|
|
37720
|
+
this.qualityLevels = this.corePlayer.getQualityLevels();
|
|
37721
|
+
if (this.qualityLevels.length)
|
|
37722
|
+
this._buildQualityMenu();
|
|
37723
|
+
}, 100);
|
|
37724
|
+
}
|
|
37725
|
+
},
|
|
37726
|
+
onError: (err) => {
|
|
37727
|
+
this._showError(err);
|
|
37728
|
+
config.onError?.(err);
|
|
37729
|
+
},
|
|
37730
|
+
onQualityChange: (quality) => {
|
|
37731
|
+
const idx = this.qualityLevels.findIndex(l => l.name === quality);
|
|
37732
|
+
this.currentQualityIdx = idx;
|
|
37733
|
+
this._refreshQualityMenu();
|
|
37734
|
+
config.onQualityChange?.(quality);
|
|
37735
|
+
},
|
|
37736
|
+
onStateChange: (state) => {
|
|
37737
|
+
this._onStateChange(state);
|
|
37738
|
+
config.onStateChange?.(state);
|
|
37739
|
+
},
|
|
37740
|
+
});
|
|
37741
|
+
video.addEventListener('timeupdate', () => {
|
|
37742
|
+
config.onTimeUpdate?.(video.currentTime);
|
|
37743
|
+
this._onTimeUpdate();
|
|
37744
|
+
});
|
|
37745
|
+
video.addEventListener('loadedmetadata', () => { this.duration = video.duration || 0; this._onTimeUpdate(); });
|
|
37746
|
+
video.addEventListener('durationchange', () => { this.duration = video.duration || 0; });
|
|
37747
|
+
video.addEventListener('progress', () => {
|
|
37748
|
+
if (video.buffered.length > 0) {
|
|
37749
|
+
this.bufferedEnd = video.buffered.end(video.buffered.length - 1);
|
|
37750
|
+
this._updateSeekBar();
|
|
37751
|
+
}
|
|
37752
|
+
});
|
|
37753
|
+
video.addEventListener('volumechange', () => this._onVolumeChange());
|
|
37754
|
+
video.addEventListener('ended', () => {
|
|
37755
|
+
config.onEnded?.();
|
|
37756
|
+
this._showControls(true);
|
|
37757
|
+
this._renderPlayBtn();
|
|
37758
|
+
});
|
|
37759
|
+
video.addEventListener('ratechange', () => {
|
|
37760
|
+
this.playbackRate = video.playbackRate;
|
|
37761
|
+
this.speedLabel.textContent = this.playbackRate === 1 ? '1×' : `${this.playbackRate}×`;
|
|
37762
|
+
});
|
|
37763
|
+
this.playBtn.addEventListener('click', (e) => { e.stopPropagation(); this._togglePlay(); });
|
|
37764
|
+
this.volBtn.addEventListener('click', (e) => { e.stopPropagation(); this._toggleMute(); });
|
|
37765
|
+
this.volSlider.addEventListener('input', () => {
|
|
37766
|
+
video.volume = parseFloat(this.volSlider.value);
|
|
37767
|
+
video.muted = video.volume === 0;
|
|
37768
|
+
});
|
|
37769
|
+
this.seekWrap.addEventListener('mousedown', (e) => {
|
|
37770
|
+
e.preventDefault();
|
|
37771
|
+
this._startSeekDrag();
|
|
37772
|
+
this._seekTo(e.clientX);
|
|
37773
|
+
window.addEventListener('mousemove', this._seekMouseMoveBound);
|
|
37774
|
+
window.addEventListener('mouseup', this._seekMouseUpBound);
|
|
37775
|
+
});
|
|
37776
|
+
this.seekWrap.addEventListener('mousemove', (e) => this._onSeekHover(e.clientX));
|
|
37777
|
+
this.seekWrap.addEventListener('mouseleave', () => { this.seekTooltip.style.opacity = '0'; });
|
|
37778
|
+
this.seekWrap.addEventListener('touchstart', (e) => {
|
|
37779
|
+
e.preventDefault();
|
|
37780
|
+
this._startSeekDrag();
|
|
37781
|
+
this._seekTo(e.touches[0].clientX);
|
|
37782
|
+
window.addEventListener('touchmove', this._seekTouchMoveBound, { passive: false });
|
|
37783
|
+
window.addEventListener('touchend', this._seekTouchEndBound);
|
|
37784
|
+
}, { passive: false });
|
|
37785
|
+
this.seekWrap.addEventListener('keydown', (e) => {
|
|
37786
|
+
if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight')
|
|
37787
|
+
return;
|
|
37788
|
+
e.preventDefault();
|
|
37789
|
+
const delta = e.key === 'ArrowLeft' ? -5 : 5;
|
|
37790
|
+
this.corePlayer.seek(Math.max(0, Math.min(this.duration, video.currentTime + delta)));
|
|
37791
|
+
this._resetHideTimer();
|
|
37792
|
+
});
|
|
37793
|
+
this.speedLabel.addEventListener('click', (e) => { e.stopPropagation(); this._toggleMenu('speed'); });
|
|
37794
|
+
this.speedMenu.addEventListener('click', (e) => {
|
|
37795
|
+
e.stopPropagation();
|
|
37796
|
+
const item = e.target.closest('[data-speed]');
|
|
37797
|
+
if (!item)
|
|
37798
|
+
return;
|
|
37799
|
+
video.playbackRate = parseFloat(item.dataset['speed']);
|
|
37800
|
+
this._closeMenus();
|
|
37801
|
+
});
|
|
37802
|
+
this.settingsBtn.addEventListener('click', (e) => { e.stopPropagation(); this._toggleMenu('quality'); });
|
|
37803
|
+
this.qualityMenu.addEventListener('click', (e) => {
|
|
37804
|
+
e.stopPropagation();
|
|
37805
|
+
const item = e.target.closest('[data-quality]');
|
|
37806
|
+
if (!item)
|
|
37807
|
+
return;
|
|
37808
|
+
const idx = parseInt(item.dataset['quality']);
|
|
37809
|
+
this.corePlayer.setQuality(idx);
|
|
37810
|
+
this.currentQualityIdx = idx;
|
|
37811
|
+
this._refreshQualityMenu();
|
|
37812
|
+
this._closeMenus();
|
|
37813
|
+
});
|
|
37814
|
+
this.clickArea.addEventListener('click', (e) => { this._addRipple(e); this._togglePlay(); });
|
|
37815
|
+
this.centerPlay.addEventListener('click', (e) => { e.stopPropagation(); this._togglePlay(); });
|
|
37816
|
+
this.fsBtn.addEventListener('click', (e) => { e.stopPropagation(); this._toggleFullscreen(); });
|
|
37817
|
+
document.addEventListener('fullscreenchange', this._onFsChangeBound);
|
|
37818
|
+
this.root.addEventListener('mousemove', () => this._resetHideTimer());
|
|
37819
|
+
this.root.addEventListener('touchstart', () => this._resetHideTimer(), { passive: true });
|
|
37820
|
+
this.root.addEventListener('mouseleave', () => {
|
|
37821
|
+
if (this.playerState === exports.PlayerState.PLAYING)
|
|
37822
|
+
this._showControls(false);
|
|
37823
|
+
});
|
|
37824
|
+
this.root.addEventListener('click', () => this._closeMenus());
|
|
37825
|
+
this.root.addEventListener('keydown', (e) => this._onKeyDown(e));
|
|
37826
|
+
}
|
|
37827
|
+
_onStateChange(state) {
|
|
37828
|
+
this.playerState = state;
|
|
37829
|
+
const loading = state === exports.PlayerState.LOADING || state === exports.PlayerState.BUFFERING;
|
|
37830
|
+
const idle = state === exports.PlayerState.IDLE;
|
|
37831
|
+
const ended = this.videoEl.ended;
|
|
37832
|
+
this.spinner.classList.toggle('gvp-hidden', !loading);
|
|
37833
|
+
const showCenter = (idle || ended) && !loading;
|
|
37834
|
+
this.centerPlay.classList.toggle('gvp-hidden', !showCenter);
|
|
37835
|
+
if (showCenter) {
|
|
37836
|
+
this.centerPlayBtn.innerHTML = '';
|
|
37837
|
+
this.centerPlayBtn.appendChild(svgEl(ended ? ICON.replay : ICON.play, 32, 32));
|
|
37838
|
+
this.centerPlay.setAttribute('aria-label', ended ? 'Replay' : 'Play');
|
|
37839
|
+
}
|
|
37840
|
+
this.clickArea.classList.toggle('gvp-hidden', idle);
|
|
37841
|
+
if (state !== exports.PlayerState.PLAYING) {
|
|
37842
|
+
this._showControls(true);
|
|
37843
|
+
if (this.hideTimer) {
|
|
37844
|
+
clearTimeout(this.hideTimer);
|
|
37845
|
+
this.hideTimer = null;
|
|
37846
|
+
}
|
|
37847
|
+
}
|
|
37848
|
+
else {
|
|
37849
|
+
this._resetHideTimer();
|
|
37850
|
+
}
|
|
37851
|
+
this._renderPlayBtn();
|
|
37301
37852
|
}
|
|
37302
|
-
|
|
37303
|
-
const
|
|
37304
|
-
|
|
37305
|
-
|
|
37306
|
-
if (!element || element.tagName !== 'VIDEO') {
|
|
37307
|
-
throw new Error('Invalid video element provided');
|
|
37853
|
+
_togglePlay() {
|
|
37854
|
+
const v = this.videoEl;
|
|
37855
|
+
if (v.paused || v.ended) {
|
|
37856
|
+
this.corePlayer.play().catch(() => { });
|
|
37308
37857
|
}
|
|
37309
|
-
|
|
37858
|
+
else {
|
|
37859
|
+
this.corePlayer.pause();
|
|
37860
|
+
}
|
|
37861
|
+
this._resetHideTimer();
|
|
37862
|
+
}
|
|
37863
|
+
_renderPlayBtn() {
|
|
37864
|
+
const ended = this.videoEl.ended;
|
|
37865
|
+
const playing = this.playerState === exports.PlayerState.PLAYING;
|
|
37866
|
+
const icon = ended ? ICON.replay : playing ? ICON.pause : ICON.play;
|
|
37867
|
+
const label = playing ? 'Pause (k)' : 'Play (k)';
|
|
37868
|
+
this.playBtn.innerHTML = '';
|
|
37869
|
+
this.playBtn.appendChild(svgEl(icon));
|
|
37870
|
+
this.playBtn.setAttribute('aria-label', label);
|
|
37871
|
+
this.playBtn.title = label;
|
|
37872
|
+
}
|
|
37873
|
+
_toggleMute() { this.videoEl.muted = !this.videoEl.muted; }
|
|
37874
|
+
_onVolumeChange() {
|
|
37875
|
+
const v = this.videoEl;
|
|
37876
|
+
const muted = v.muted || v.volume === 0;
|
|
37877
|
+
this.volBtn.innerHTML = '';
|
|
37878
|
+
this.volBtn.appendChild(svgEl(muted ? ICON.volMute : ICON.volHigh, 18, 18));
|
|
37879
|
+
this.volBtn.setAttribute('aria-label', muted ? 'Unmute' : 'Mute');
|
|
37880
|
+
this.volSlider.value = String(muted ? 0 : v.volume);
|
|
37881
|
+
}
|
|
37882
|
+
_startSeekDrag() {
|
|
37883
|
+
this.seekDragging = true;
|
|
37884
|
+
this.seekWrap.classList.add('gvp-dragging');
|
|
37885
|
+
}
|
|
37886
|
+
_endSeekDrag() {
|
|
37887
|
+
this.seekDragging = false;
|
|
37888
|
+
this.seekWrap.classList.remove('gvp-dragging');
|
|
37889
|
+
window.removeEventListener('mousemove', this._seekMouseMoveBound);
|
|
37890
|
+
window.removeEventListener('mouseup', this._seekMouseUpBound);
|
|
37891
|
+
window.removeEventListener('touchmove', this._seekTouchMoveBound);
|
|
37892
|
+
window.removeEventListener('touchend', this._seekTouchEndBound);
|
|
37893
|
+
this._resetHideTimer();
|
|
37894
|
+
}
|
|
37895
|
+
_seekTo(clientX) {
|
|
37896
|
+
if (!this.duration)
|
|
37897
|
+
return;
|
|
37898
|
+
const rect = this.seekWrap.getBoundingClientRect();
|
|
37899
|
+
const pct = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
|
|
37900
|
+
this.corePlayer.seek(pct * this.duration);
|
|
37901
|
+
}
|
|
37902
|
+
_onSeekHover(clientX) {
|
|
37903
|
+
if (!this.duration)
|
|
37904
|
+
return;
|
|
37905
|
+
const rect = this.seekWrap.getBoundingClientRect();
|
|
37906
|
+
const pct = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
|
|
37907
|
+
this.seekTooltip.textContent = formatTime(pct * this.duration);
|
|
37908
|
+
this.seekTooltip.style.left = `${pct * 100}%`;
|
|
37909
|
+
this.seekTooltip.style.opacity = '1';
|
|
37910
|
+
}
|
|
37911
|
+
_onTimeUpdate() {
|
|
37912
|
+
const ct = this.videoEl.currentTime;
|
|
37913
|
+
const dur = this.duration;
|
|
37914
|
+
this.timeEl.textContent = `${formatTime(ct)} / ${formatTime(dur)}`;
|
|
37915
|
+
this._updateSeekBar();
|
|
37916
|
+
const pct = dur > 0 ? Math.round((ct / dur) * 100) : 0;
|
|
37917
|
+
this.seekWrap.setAttribute('aria-valuenow', String(pct));
|
|
37918
|
+
this.seekWrap.setAttribute('aria-valuetext', `${formatTime(ct)} of ${formatTime(dur)}`);
|
|
37919
|
+
}
|
|
37920
|
+
_updateSeekBar() {
|
|
37921
|
+
const ct = this.videoEl.currentTime;
|
|
37922
|
+
const dur = this.duration;
|
|
37923
|
+
if (!dur)
|
|
37924
|
+
return;
|
|
37925
|
+
const pPct = (ct / dur) * 100;
|
|
37926
|
+
const bPct = (this.bufferedEnd / dur) * 100;
|
|
37927
|
+
this.seekProgress.style.width = `${pPct}%`;
|
|
37928
|
+
this.seekBuffered.style.width = `${bPct}%`;
|
|
37929
|
+
this.seekThumb.style.left = `${pPct}%`;
|
|
37930
|
+
}
|
|
37931
|
+
_buildQualityMenu() {
|
|
37932
|
+
if (!this.qualityLevels.length)
|
|
37933
|
+
return;
|
|
37934
|
+
this.settingsBtn.classList.remove('gvp-hidden');
|
|
37935
|
+
this.qualityMenu.innerHTML = '';
|
|
37936
|
+
const title = el('div', 'gvp-menu-title');
|
|
37937
|
+
title.setAttribute('aria-hidden', 'true');
|
|
37938
|
+
title.textContent = 'Quality';
|
|
37939
|
+
const autoItem = el('div', 'gvp-menu-item gvp-menu-item-active');
|
|
37940
|
+
autoItem.setAttribute('role', 'menuitemradio');
|
|
37941
|
+
autoItem.setAttribute('aria-checked', 'true');
|
|
37942
|
+
autoItem.dataset['quality'] = '-1';
|
|
37943
|
+
autoItem.textContent = 'Auto';
|
|
37944
|
+
const checkEl = el('span', 'gvp-menu-check');
|
|
37945
|
+
checkEl.setAttribute('aria-hidden', 'true');
|
|
37946
|
+
checkEl.textContent = '✓';
|
|
37947
|
+
autoItem.appendChild(checkEl);
|
|
37948
|
+
this.qualityMenu.append(title, autoItem, el('div', 'gvp-menu-sep'));
|
|
37949
|
+
[...this.qualityLevels].reverse().forEach(q => {
|
|
37950
|
+
const item = el('div', 'gvp-menu-item');
|
|
37951
|
+
item.setAttribute('role', 'menuitemradio');
|
|
37952
|
+
item.setAttribute('aria-checked', 'false');
|
|
37953
|
+
item.dataset['quality'] = String(q.index);
|
|
37954
|
+
item.textContent = q.name;
|
|
37955
|
+
this.qualityMenu.appendChild(item);
|
|
37956
|
+
});
|
|
37957
|
+
this.currentQualityIdx = -1;
|
|
37958
|
+
}
|
|
37959
|
+
_refreshQualityMenu() {
|
|
37960
|
+
this.qualityMenu.querySelectorAll('[data-quality]').forEach(item => {
|
|
37961
|
+
const active = parseInt(item.dataset['quality']) === this.currentQualityIdx;
|
|
37962
|
+
item.className = active ? 'gvp-menu-item gvp-menu-item-active' : 'gvp-menu-item';
|
|
37963
|
+
item.setAttribute('aria-checked', String(active));
|
|
37964
|
+
const existing = item.querySelector('.gvp-menu-check');
|
|
37965
|
+
if (active && !existing) {
|
|
37966
|
+
const c = el('span', 'gvp-menu-check');
|
|
37967
|
+
c.setAttribute('aria-hidden', 'true');
|
|
37968
|
+
c.textContent = '✓';
|
|
37969
|
+
item.appendChild(c);
|
|
37970
|
+
}
|
|
37971
|
+
else if (!active && existing) {
|
|
37972
|
+
existing.remove();
|
|
37973
|
+
}
|
|
37974
|
+
});
|
|
37310
37975
|
}
|
|
37976
|
+
_refreshSpeedMenu() {
|
|
37977
|
+
this.speedMenu.querySelectorAll('[data-speed]').forEach(item => {
|
|
37978
|
+
const active = parseFloat(item.dataset['speed']) === this.playbackRate;
|
|
37979
|
+
item.className = active ? 'gvp-menu-item gvp-menu-item-active' : 'gvp-menu-item';
|
|
37980
|
+
item.setAttribute('aria-checked', String(active));
|
|
37981
|
+
const existing = item.querySelector('.gvp-menu-check');
|
|
37982
|
+
if (active && !existing) {
|
|
37983
|
+
const c = el('span', 'gvp-menu-check');
|
|
37984
|
+
c.setAttribute('aria-hidden', 'true');
|
|
37985
|
+
c.textContent = '✓';
|
|
37986
|
+
item.appendChild(c);
|
|
37987
|
+
}
|
|
37988
|
+
else if (!active && existing) {
|
|
37989
|
+
existing.remove();
|
|
37990
|
+
}
|
|
37991
|
+
});
|
|
37992
|
+
}
|
|
37993
|
+
_toggleMenu(which) {
|
|
37994
|
+
const same = this.openMenu === which;
|
|
37995
|
+
this._closeMenus();
|
|
37996
|
+
if (!same) {
|
|
37997
|
+
this.openMenu = which;
|
|
37998
|
+
if (which === 'speed') {
|
|
37999
|
+
this._refreshSpeedMenu();
|
|
38000
|
+
this.speedMenu.classList.remove('gvp-hidden');
|
|
38001
|
+
}
|
|
38002
|
+
else {
|
|
38003
|
+
this._refreshQualityMenu();
|
|
38004
|
+
this.qualityMenu.classList.remove('gvp-hidden');
|
|
38005
|
+
}
|
|
38006
|
+
}
|
|
38007
|
+
}
|
|
38008
|
+
_closeMenus() {
|
|
38009
|
+
this.openMenu = null;
|
|
38010
|
+
this.speedMenu.classList.add('gvp-hidden');
|
|
38011
|
+
this.qualityMenu.classList.add('gvp-hidden');
|
|
38012
|
+
}
|
|
38013
|
+
_showControls(visible) {
|
|
38014
|
+
this.controls.classList.toggle('gvp-controls-hidden', !visible);
|
|
38015
|
+
this.badge.classList.toggle('gvp-badge-hidden', !visible);
|
|
38016
|
+
}
|
|
38017
|
+
_resetHideTimer() {
|
|
38018
|
+
this._showControls(true);
|
|
38019
|
+
if (this.hideTimer)
|
|
38020
|
+
clearTimeout(this.hideTimer);
|
|
38021
|
+
this.hideTimer = setTimeout(() => {
|
|
38022
|
+
if (this.playerState === exports.PlayerState.PLAYING && !this.openMenu) {
|
|
38023
|
+
this._showControls(false);
|
|
38024
|
+
}
|
|
38025
|
+
}, 2800);
|
|
38026
|
+
}
|
|
38027
|
+
_toggleFullscreen() {
|
|
38028
|
+
if (document.fullscreenElement) {
|
|
38029
|
+
document.exitFullscreen().catch(() => { });
|
|
38030
|
+
}
|
|
38031
|
+
else {
|
|
38032
|
+
this.root.requestFullscreen().catch(() => { });
|
|
38033
|
+
}
|
|
38034
|
+
}
|
|
38035
|
+
_onFsChange() {
|
|
38036
|
+
const fs = !!document.fullscreenElement;
|
|
38037
|
+
this.fsBtn.innerHTML = '';
|
|
38038
|
+
this.fsBtn.appendChild(svgEl(fs ? ICON.fsExit : ICON.fsEnter, 18, 18));
|
|
38039
|
+
this.fsBtn.setAttribute('aria-label', fs ? 'Exit fullscreen' : 'Enter fullscreen');
|
|
38040
|
+
this.fsBtn.title = fs ? 'Exit fullscreen (f)' : 'Enter fullscreen (f)';
|
|
38041
|
+
}
|
|
38042
|
+
_showError(err) {
|
|
38043
|
+
this.errorOverlay.children[1].textContent = err.code;
|
|
38044
|
+
this.errorOverlay.children[2].textContent = err.message;
|
|
38045
|
+
this.errorOverlay.classList.remove('gvp-hidden');
|
|
38046
|
+
this.controls.classList.add('gvp-hidden');
|
|
38047
|
+
}
|
|
38048
|
+
_renderWatermark(text) {
|
|
38049
|
+
this.watermarkDiv.innerHTML = '';
|
|
38050
|
+
for (let i = 0; i < 20; i++) {
|
|
38051
|
+
const span = el('span', 'gvp-watermark-text');
|
|
38052
|
+
span.textContent = text;
|
|
38053
|
+
span.style.left = `${(i % 4) * 26 + (Math.floor(i / 4) % 2) * 13}%`;
|
|
38054
|
+
span.style.top = `${Math.floor(i / 4) * 22}%`;
|
|
38055
|
+
this.watermarkDiv.appendChild(span);
|
|
38056
|
+
}
|
|
38057
|
+
}
|
|
38058
|
+
_addRipple(e) {
|
|
38059
|
+
const rect = this.root.getBoundingClientRect();
|
|
38060
|
+
const d = document.createElement('div');
|
|
38061
|
+
d.className = 'gvp-ripple';
|
|
38062
|
+
const sz = 60;
|
|
38063
|
+
d.style.cssText = `width:${sz}px;height:${sz}px;left:${e.clientX - rect.left - sz / 2}px;top:${e.clientY - rect.top - sz / 2}px`;
|
|
38064
|
+
this.root.appendChild(d);
|
|
38065
|
+
setTimeout(() => d.remove(), 600);
|
|
38066
|
+
}
|
|
38067
|
+
_onKeyDown(e) {
|
|
38068
|
+
switch (e.code) {
|
|
38069
|
+
case 'Space':
|
|
38070
|
+
case 'KeyK':
|
|
38071
|
+
e.preventDefault();
|
|
38072
|
+
this._togglePlay();
|
|
38073
|
+
break;
|
|
38074
|
+
case 'ArrowLeft':
|
|
38075
|
+
if (document.activeElement !== this.seekWrap) {
|
|
38076
|
+
e.preventDefault();
|
|
38077
|
+
this.corePlayer.seek(Math.max(0, this.videoEl.currentTime - 5));
|
|
38078
|
+
}
|
|
38079
|
+
break;
|
|
38080
|
+
case 'ArrowRight':
|
|
38081
|
+
if (document.activeElement !== this.seekWrap) {
|
|
38082
|
+
e.preventDefault();
|
|
38083
|
+
this.corePlayer.seek(Math.min(this.duration, this.videoEl.currentTime + 5));
|
|
38084
|
+
}
|
|
38085
|
+
break;
|
|
38086
|
+
case 'ArrowUp':
|
|
38087
|
+
e.preventDefault();
|
|
38088
|
+
this.videoEl.volume = Math.min(1, this.videoEl.volume + 0.1);
|
|
38089
|
+
break;
|
|
38090
|
+
case 'ArrowDown':
|
|
38091
|
+
e.preventDefault();
|
|
38092
|
+
this.videoEl.volume = Math.max(0, this.videoEl.volume - 0.1);
|
|
38093
|
+
break;
|
|
38094
|
+
case 'KeyM':
|
|
38095
|
+
e.preventDefault();
|
|
38096
|
+
this._toggleMute();
|
|
38097
|
+
break;
|
|
38098
|
+
case 'KeyF':
|
|
38099
|
+
e.preventDefault();
|
|
38100
|
+
this._toggleFullscreen();
|
|
38101
|
+
break;
|
|
38102
|
+
}
|
|
38103
|
+
}
|
|
38104
|
+
play() { return this.corePlayer.play(); }
|
|
38105
|
+
pause() { return this.corePlayer.pause(); }
|
|
38106
|
+
seek(t) { return this.corePlayer.seek(t); }
|
|
38107
|
+
getCurrentTime() { return this.corePlayer.getCurrentTime(); }
|
|
38108
|
+
getDuration() { return this.corePlayer.getDuration(); }
|
|
38109
|
+
getVolume() { return this.corePlayer.getVolume(); }
|
|
38110
|
+
setVolume(v) { return this.corePlayer.setVolume(v); }
|
|
38111
|
+
getQualityLevels() { return this.corePlayer.getQualityLevels(); }
|
|
38112
|
+
getCurrentQuality() { return this.corePlayer.getCurrentQuality(); }
|
|
38113
|
+
setQuality(i) { return this.corePlayer.setQuality(i); }
|
|
38114
|
+
getState() { return this.corePlayer.getState(); }
|
|
38115
|
+
getVideoElement() { return this.videoEl; }
|
|
38116
|
+
destroy() {
|
|
38117
|
+
if (this.hideTimer)
|
|
38118
|
+
clearTimeout(this.hideTimer);
|
|
38119
|
+
document.removeEventListener('fullscreenchange', this._onFsChangeBound);
|
|
38120
|
+
window.removeEventListener('mousemove', this._seekMouseMoveBound);
|
|
38121
|
+
window.removeEventListener('mouseup', this._seekMouseUpBound);
|
|
38122
|
+
window.removeEventListener('touchmove', this._seekTouchMoveBound);
|
|
38123
|
+
window.removeEventListener('touchend', this._seekTouchEndBound);
|
|
38124
|
+
this.corePlayer.destroy();
|
|
38125
|
+
this.root.remove();
|
|
38126
|
+
}
|
|
38127
|
+
}
|
|
38128
|
+
|
|
38129
|
+
class GuardVideoPlayer {
|
|
38130
|
+
constructor(ui) {
|
|
38131
|
+
this.ui = ui;
|
|
38132
|
+
}
|
|
38133
|
+
static create(containerId, videoId, config) {
|
|
38134
|
+
const container = document.getElementById(containerId);
|
|
38135
|
+
if (!container) {
|
|
38136
|
+
throw new Error(`GuardVideoPlayer: element '#${containerId}' not found`);
|
|
38137
|
+
}
|
|
38138
|
+
return new GuardVideoPlayer(new PlayerUI(container, videoId, config));
|
|
38139
|
+
}
|
|
38140
|
+
static createInElement(container, videoId, config) {
|
|
38141
|
+
return new GuardVideoPlayer(new PlayerUI(container, videoId, config));
|
|
38142
|
+
}
|
|
38143
|
+
play() { return this.ui.play(); }
|
|
38144
|
+
pause() { return this.ui.pause(); }
|
|
38145
|
+
seek(time) { return this.ui.seek(time); }
|
|
38146
|
+
getCurrentTime() { return this.ui.getCurrentTime(); }
|
|
38147
|
+
getDuration() { return this.ui.getDuration(); }
|
|
38148
|
+
getVolume() { return this.ui.getVolume(); }
|
|
38149
|
+
setVolume(volume) { return this.ui.setVolume(volume); }
|
|
38150
|
+
getQualityLevels() { return this.ui.getQualityLevels(); }
|
|
38151
|
+
getCurrentQuality() { return this.ui.getCurrentQuality(); }
|
|
38152
|
+
setQuality(idx) { return this.ui.setQuality(idx); }
|
|
38153
|
+
getState() { return this.ui.getState(); }
|
|
38154
|
+
getVideoElement() { return this.ui.getVideoElement(); }
|
|
38155
|
+
destroy() { return this.ui.destroy(); }
|
|
37311
38156
|
}
|
|
37312
38157
|
if (typeof window !== 'undefined') {
|
|
37313
38158
|
window.GuardVideoPlayer = GuardVideoPlayer;
|
|
@@ -37315,6 +38160,7 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37315
38160
|
}
|
|
37316
38161
|
|
|
37317
38162
|
exports.GuardVideoPlayer = GuardVideoPlayer;
|
|
38163
|
+
exports.PlayerUI = PlayerUI;
|
|
37318
38164
|
|
|
37319
38165
|
return exports;
|
|
37320
38166
|
|