@guardvideo/player-sdk 2.0.0 → 2.1.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/dist/core/player.d.ts +0 -16
- package/dist/core/player.d.ts.map +1 -1
- package/dist/core/types.d.ts +1 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.esm.js +307 -359
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +307 -359
- package/dist/index.js.map +1 -1
- package/dist/ui/PlayerUI.d.ts +12 -0
- package/dist/ui/PlayerUI.d.ts.map +1 -1
- package/dist/vanilla/guardvideo-player.js +307 -359
- package/dist/vanilla/guardvideo-player.js.map +1 -1
- package/dist/vanilla/guardvideo-player.min.js +1 -1
- package/dist/vanilla/guardvideo-player.min.js.map +1 -1
- package/package.json +1 -1
|
@@ -36657,23 +36657,12 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
36657
36657
|
let GuardVideoPlayer$1 = class GuardVideoPlayer {
|
|
36658
36658
|
constructor(videoElement, videoId, config) {
|
|
36659
36659
|
this.videoId = videoId;
|
|
36660
|
-
this.container = null;
|
|
36661
36660
|
this.hls = null;
|
|
36662
36661
|
this.state = exports.PlayerState.IDLE;
|
|
36663
36662
|
this.embedToken = null;
|
|
36664
36663
|
this.currentQuality = null;
|
|
36665
|
-
this.ctxMenu = null;
|
|
36666
|
-
this.ctxStyleTag = null;
|
|
36667
|
-
this.watermarkEl = null;
|
|
36668
|
-
this.watermarkObserver = null;
|
|
36669
|
-
this._onCtx = this.handleContextMenu.bind(this);
|
|
36670
|
-
this._onDocClick = this.hideContextMenu.bind(this);
|
|
36671
|
-
this._onKeyDown = this.handleKeyDown.bind(this);
|
|
36672
36664
|
this._onRateChange = this.enforceMaxRate.bind(this);
|
|
36673
|
-
this._onSelectStart = (e) => e.preventDefault();
|
|
36674
|
-
this._onDragStart = (e) => e.preventDefault();
|
|
36675
36665
|
this.videoElement = videoElement;
|
|
36676
|
-
this.container = videoElement.parentElement;
|
|
36677
36666
|
this.config = {
|
|
36678
36667
|
embedTokenEndpoint: config.embedTokenEndpoint,
|
|
36679
36668
|
apiBaseUrl: config.apiBaseUrl || '',
|
|
@@ -36693,6 +36682,7 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
36693
36682
|
onError: config.onError || (() => { }),
|
|
36694
36683
|
onQualityChange: config.onQualityChange || (() => { }),
|
|
36695
36684
|
onStateChange: config.onStateChange || (() => { }),
|
|
36685
|
+
onWatermark: config.onWatermark || (() => { }),
|
|
36696
36686
|
};
|
|
36697
36687
|
this.log('Initializing GuardVideo Player', { videoId, config });
|
|
36698
36688
|
if (!this.checkAllowedDomain())
|
|
@@ -36702,17 +36692,17 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
36702
36692
|
}
|
|
36703
36693
|
log(message, data) {
|
|
36704
36694
|
if (this.config.debug) {
|
|
36705
|
-
console.log(
|
|
36695
|
+
console.log('[GuardVideoPlayer] ' + message, data || '');
|
|
36706
36696
|
}
|
|
36707
36697
|
}
|
|
36708
36698
|
error(message, data) {
|
|
36709
|
-
console.error(
|
|
36699
|
+
console.error('[GuardVideoPlayer] ' + message, data || '');
|
|
36710
36700
|
}
|
|
36711
36701
|
setState(newState) {
|
|
36712
36702
|
if (this.state !== newState) {
|
|
36713
36703
|
this.state = newState;
|
|
36714
36704
|
this.config.onStateChange(newState);
|
|
36715
|
-
this.log(
|
|
36705
|
+
this.log('State changed to: ' + newState);
|
|
36716
36706
|
}
|
|
36717
36707
|
}
|
|
36718
36708
|
checkAllowedDomain() {
|
|
@@ -36720,11 +36710,11 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
36720
36710
|
if (!domains || domains.length === 0)
|
|
36721
36711
|
return true;
|
|
36722
36712
|
const currentOrigin = typeof window !== 'undefined' ? window.location.origin : '';
|
|
36723
|
-
const allowed = domains.some((d) => currentOrigin === d || currentOrigin.endsWith(
|
|
36713
|
+
const allowed = domains.some((d) => currentOrigin === d || currentOrigin.endsWith('.' + d.replace(/^https?:\/\//, '')));
|
|
36724
36714
|
if (!allowed) {
|
|
36725
36715
|
this.handleError({
|
|
36726
36716
|
code: 'DOMAIN_NOT_ALLOWED',
|
|
36727
|
-
message:
|
|
36717
|
+
message: 'This player is not authorized to run on ' + currentOrigin,
|
|
36728
36718
|
fatal: true,
|
|
36729
36719
|
});
|
|
36730
36720
|
return false;
|
|
@@ -36733,294 +36723,24 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
36733
36723
|
}
|
|
36734
36724
|
applySecurity() {
|
|
36735
36725
|
const sec = this.config.security;
|
|
36736
|
-
const target = this.container || this.videoElement;
|
|
36737
|
-
target.addEventListener('contextmenu', this._onCtx);
|
|
36738
|
-
document.addEventListener('click', this._onDocClick);
|
|
36739
|
-
if (sec.disableSelection) {
|
|
36740
|
-
target.addEventListener('selectstart', this._onSelectStart);
|
|
36741
|
-
target.style.userSelect = 'none';
|
|
36742
|
-
target.style.webkitUserSelect = 'none';
|
|
36743
|
-
}
|
|
36744
|
-
if (sec.disableDrag) {
|
|
36745
|
-
this.videoElement.addEventListener('dragstart', this._onDragStart);
|
|
36746
|
-
this.videoElement.draggable = false;
|
|
36747
|
-
}
|
|
36748
36726
|
if (sec.disablePiP) {
|
|
36749
36727
|
this.videoElement.disablePictureInPicture = true;
|
|
36750
36728
|
}
|
|
36751
36729
|
if (sec.disableScreenCapture) {
|
|
36752
|
-
if ('mediaKeys' in this.videoElement &&
|
|
36730
|
+
if ('mediaKeys' in this.videoElement &&
|
|
36731
|
+
typeof navigator.requestMediaKeySystemAccess === 'function') {
|
|
36753
36732
|
this.log('Screen-capture protection: EME hint applied');
|
|
36754
36733
|
}
|
|
36755
|
-
target.style.setProperty('-webkit-app-region', 'no-drag');
|
|
36756
|
-
}
|
|
36757
|
-
if (sec.blockDevTools) {
|
|
36758
|
-
document.addEventListener('keydown', this._onKeyDown);
|
|
36759
36734
|
}
|
|
36760
36735
|
if (sec.maxPlaybackRate) {
|
|
36761
36736
|
this.videoElement.addEventListener('ratechange', this._onRateChange);
|
|
36762
36737
|
}
|
|
36763
|
-
if (sec.enableWatermark && sec.watermarkText && this.container) {
|
|
36764
|
-
this.createWatermark(sec.watermarkText);
|
|
36765
|
-
}
|
|
36766
|
-
this.injectProtectiveStyles();
|
|
36767
|
-
}
|
|
36768
|
-
handleContextMenu(e) {
|
|
36769
|
-
e.preventDefault();
|
|
36770
|
-
e.stopPropagation();
|
|
36771
|
-
const sec = this.config.security;
|
|
36772
|
-
if (sec.disableRightClick)
|
|
36773
|
-
return;
|
|
36774
|
-
const me = e;
|
|
36775
|
-
this.showContextMenu(me.clientX, me.clientY);
|
|
36776
|
-
}
|
|
36777
|
-
showContextMenu(x, y) {
|
|
36778
|
-
this.hideContextMenu();
|
|
36779
|
-
const branding = this.config.branding;
|
|
36780
|
-
const extraItems = this.config.contextMenuItems;
|
|
36781
|
-
const menu = document.createElement('div');
|
|
36782
|
-
menu.className = 'gv-ctx-menu';
|
|
36783
|
-
menu.setAttribute('role', 'menu');
|
|
36784
|
-
const header = document.createElement('a');
|
|
36785
|
-
header.className = 'gv-ctx-header';
|
|
36786
|
-
header.href = branding.url;
|
|
36787
|
-
header.target = '_blank';
|
|
36788
|
-
header.rel = 'noopener noreferrer';
|
|
36789
|
-
header.setAttribute('role', 'menuitem');
|
|
36790
|
-
if (branding.logoUrl) {
|
|
36791
|
-
const logo = document.createElement('img');
|
|
36792
|
-
logo.src = branding.logoUrl;
|
|
36793
|
-
logo.alt = branding.name;
|
|
36794
|
-
logo.className = 'gv-ctx-logo';
|
|
36795
|
-
logo.width = 20;
|
|
36796
|
-
logo.height = 20;
|
|
36797
|
-
header.appendChild(logo);
|
|
36798
|
-
}
|
|
36799
|
-
else {
|
|
36800
|
-
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
36801
|
-
svg.setAttribute('width', '18');
|
|
36802
|
-
svg.setAttribute('height', '18');
|
|
36803
|
-
svg.setAttribute('viewBox', '0 0 24 24');
|
|
36804
|
-
svg.setAttribute('fill', 'none');
|
|
36805
|
-
svg.setAttribute('stroke', branding.accentColor);
|
|
36806
|
-
svg.setAttribute('stroke-width', '2');
|
|
36807
|
-
svg.setAttribute('stroke-linecap', 'round');
|
|
36808
|
-
svg.setAttribute('stroke-linejoin', 'round');
|
|
36809
|
-
svg.innerHTML = '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>';
|
|
36810
|
-
header.appendChild(svg);
|
|
36811
|
-
}
|
|
36812
|
-
const nameSpan = document.createElement('span');
|
|
36813
|
-
nameSpan.className = 'gv-ctx-brand-name';
|
|
36814
|
-
nameSpan.textContent = branding.name;
|
|
36815
|
-
header.appendChild(nameSpan);
|
|
36816
|
-
const tagSpan = document.createElement('span');
|
|
36817
|
-
tagSpan.className = 'gv-ctx-tag';
|
|
36818
|
-
tagSpan.textContent = 'Secure Video Player';
|
|
36819
|
-
header.appendChild(tagSpan);
|
|
36820
|
-
menu.appendChild(header);
|
|
36821
|
-
if (extraItems.length > 0) {
|
|
36822
|
-
extraItems.forEach((item) => {
|
|
36823
|
-
if (item.separator) {
|
|
36824
|
-
const sep = document.createElement('div');
|
|
36825
|
-
sep.className = 'gv-ctx-sep';
|
|
36826
|
-
menu.appendChild(sep);
|
|
36827
|
-
}
|
|
36828
|
-
const row = document.createElement('div');
|
|
36829
|
-
row.className = 'gv-ctx-item';
|
|
36830
|
-
row.setAttribute('role', 'menuitem');
|
|
36831
|
-
row.tabIndex = 0;
|
|
36832
|
-
if (item.icon) {
|
|
36833
|
-
const ico = document.createElement('img');
|
|
36834
|
-
ico.src = item.icon;
|
|
36835
|
-
ico.width = 14;
|
|
36836
|
-
ico.height = 14;
|
|
36837
|
-
ico.className = 'gv-ctx-item-icon';
|
|
36838
|
-
row.appendChild(ico);
|
|
36839
|
-
}
|
|
36840
|
-
const label = document.createElement('span');
|
|
36841
|
-
label.textContent = item.label;
|
|
36842
|
-
row.appendChild(label);
|
|
36843
|
-
row.addEventListener('click', (ev) => {
|
|
36844
|
-
ev.stopPropagation();
|
|
36845
|
-
this.hideContextMenu();
|
|
36846
|
-
if (item.onClick) {
|
|
36847
|
-
item.onClick();
|
|
36848
|
-
}
|
|
36849
|
-
else if (item.href) {
|
|
36850
|
-
window.open(item.href, '_blank', 'noopener,noreferrer');
|
|
36851
|
-
}
|
|
36852
|
-
});
|
|
36853
|
-
menu.appendChild(row);
|
|
36854
|
-
});
|
|
36855
|
-
}
|
|
36856
|
-
const sep = document.createElement('div');
|
|
36857
|
-
sep.className = 'gv-ctx-sep';
|
|
36858
|
-
menu.appendChild(sep);
|
|
36859
|
-
const version = document.createElement('div');
|
|
36860
|
-
version.className = 'gv-ctx-version';
|
|
36861
|
-
version.textContent = `${branding.name} Player v1.0`;
|
|
36862
|
-
menu.appendChild(version);
|
|
36863
|
-
document.body.appendChild(menu);
|
|
36864
|
-
const rect = menu.getBoundingClientRect();
|
|
36865
|
-
const vw = window.innerWidth;
|
|
36866
|
-
const vh = window.innerHeight;
|
|
36867
|
-
menu.style.left = `${x + rect.width > vw ? vw - rect.width - 8 : x}px`;
|
|
36868
|
-
menu.style.top = `${y + rect.height > vh ? vh - rect.height - 8 : y}px`;
|
|
36869
|
-
this.ctxMenu = menu;
|
|
36870
|
-
}
|
|
36871
|
-
hideContextMenu() {
|
|
36872
|
-
if (this.ctxMenu) {
|
|
36873
|
-
this.ctxMenu.remove();
|
|
36874
|
-
this.ctxMenu = null;
|
|
36875
|
-
}
|
|
36876
|
-
}
|
|
36877
|
-
injectProtectiveStyles() {
|
|
36878
|
-
if (this.ctxStyleTag)
|
|
36879
|
-
return;
|
|
36880
|
-
const branding = this.config.branding;
|
|
36881
|
-
const accent = branding.accentColor;
|
|
36882
|
-
const css = `
|
|
36883
|
-
/* GuardVideo branded context menu */
|
|
36884
|
-
.gv-ctx-menu {
|
|
36885
|
-
position: fixed;
|
|
36886
|
-
z-index: 2147483647;
|
|
36887
|
-
min-width: 220px;
|
|
36888
|
-
background: rgba(18, 18, 22, 0.96);
|
|
36889
|
-
backdrop-filter: blur(12px);
|
|
36890
|
-
-webkit-backdrop-filter: blur(12px);
|
|
36891
|
-
border: 1px solid rgba(255,255,255,0.08);
|
|
36892
|
-
border-radius: 10px;
|
|
36893
|
-
padding: 6px 0;
|
|
36894
|
-
box-shadow: 0 8px 32px rgba(0,0,0,0.45), 0 0 0 1px rgba(255,255,255,0.04);
|
|
36895
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
36896
|
-
font-size: 13px;
|
|
36897
|
-
color: #e4e4e7;
|
|
36898
|
-
user-select: none;
|
|
36899
|
-
animation: gv-ctx-in 0.12s ease-out;
|
|
36900
|
-
}
|
|
36901
|
-
@keyframes gv-ctx-in {
|
|
36902
|
-
from { opacity: 0; transform: scale(0.96); }
|
|
36903
|
-
to { opacity: 1; transform: scale(1); }
|
|
36904
|
-
}
|
|
36905
|
-
|
|
36906
|
-
.gv-ctx-header {
|
|
36907
|
-
display: flex;
|
|
36908
|
-
align-items: center;
|
|
36909
|
-
gap: 8px;
|
|
36910
|
-
padding: 8px 14px 8px 12px;
|
|
36911
|
-
text-decoration: none;
|
|
36912
|
-
color: inherit;
|
|
36913
|
-
transition: background 0.15s;
|
|
36914
|
-
border-radius: 6px 6px 0 0;
|
|
36915
|
-
}
|
|
36916
|
-
.gv-ctx-header:hover { background: rgba(255,255,255,0.06); }
|
|
36917
|
-
|
|
36918
|
-
.gv-ctx-logo { border-radius: 4px; }
|
|
36919
|
-
|
|
36920
|
-
.gv-ctx-brand-name {
|
|
36921
|
-
font-weight: 600;
|
|
36922
|
-
color: ${accent};
|
|
36923
|
-
white-space: nowrap;
|
|
36924
|
-
}
|
|
36925
|
-
|
|
36926
|
-
.gv-ctx-tag {
|
|
36927
|
-
margin-left: auto;
|
|
36928
|
-
font-size: 10px;
|
|
36929
|
-
color: rgba(255,255,255,0.35);
|
|
36930
|
-
white-space: nowrap;
|
|
36931
|
-
}
|
|
36932
|
-
|
|
36933
|
-
.gv-ctx-sep {
|
|
36934
|
-
height: 1px;
|
|
36935
|
-
margin: 4px 10px;
|
|
36936
|
-
background: rgba(255,255,255,0.07);
|
|
36937
|
-
}
|
|
36938
|
-
|
|
36939
|
-
.gv-ctx-item {
|
|
36940
|
-
display: flex;
|
|
36941
|
-
align-items: center;
|
|
36942
|
-
gap: 8px;
|
|
36943
|
-
padding: 7px 14px 7px 12px;
|
|
36944
|
-
cursor: pointer;
|
|
36945
|
-
transition: background 0.15s;
|
|
36946
|
-
}
|
|
36947
|
-
.gv-ctx-item:hover { background: rgba(255,255,255,0.06); }
|
|
36948
|
-
.gv-ctx-item-icon { border-radius: 2px; }
|
|
36949
|
-
|
|
36950
|
-
.gv-ctx-version {
|
|
36951
|
-
padding: 4px 14px 6px 12px;
|
|
36952
|
-
font-size: 10px;
|
|
36953
|
-
color: rgba(255,255,255,0.25);
|
|
36954
|
-
}
|
|
36955
|
-
|
|
36956
|
-
/* Watermark overlay */
|
|
36957
|
-
.gv-watermark {
|
|
36958
|
-
position: absolute;
|
|
36959
|
-
inset: 0;
|
|
36960
|
-
pointer-events: none;
|
|
36961
|
-
overflow: hidden;
|
|
36962
|
-
z-index: 10;
|
|
36963
|
-
}
|
|
36964
|
-
.gv-watermark-text {
|
|
36965
|
-
position: absolute;
|
|
36966
|
-
white-space: nowrap;
|
|
36967
|
-
font-size: 14px;
|
|
36968
|
-
font-family: monospace;
|
|
36969
|
-
color: rgba(255,255,255,0.07);
|
|
36970
|
-
transform: rotate(-30deg);
|
|
36971
|
-
user-select: none;
|
|
36972
|
-
pointer-events: none;
|
|
36973
|
-
}
|
|
36974
|
-
`;
|
|
36975
|
-
const tag = document.createElement('style');
|
|
36976
|
-
tag.setAttribute('data-guardvideo', 'player-styles');
|
|
36977
|
-
tag.textContent = css;
|
|
36978
|
-
document.head.appendChild(tag);
|
|
36979
|
-
this.ctxStyleTag = tag;
|
|
36980
|
-
}
|
|
36981
|
-
createWatermark(text) {
|
|
36982
|
-
if (!this.container)
|
|
36983
|
-
return;
|
|
36984
|
-
const overlay = document.createElement('div');
|
|
36985
|
-
overlay.className = 'gv-watermark';
|
|
36986
|
-
for (let row = 0; row < 5; row++) {
|
|
36987
|
-
for (let col = 0; col < 4; col++) {
|
|
36988
|
-
const span = document.createElement('span');
|
|
36989
|
-
span.className = 'gv-watermark-text';
|
|
36990
|
-
span.textContent = text;
|
|
36991
|
-
span.style.left = `${col * 28 + (row % 2) * 14}%`;
|
|
36992
|
-
span.style.top = `${row * 22}%`;
|
|
36993
|
-
overlay.appendChild(span);
|
|
36994
|
-
}
|
|
36995
|
-
}
|
|
36996
|
-
this.container.style.position = 'relative';
|
|
36997
|
-
this.container.appendChild(overlay);
|
|
36998
|
-
this.watermarkEl = overlay;
|
|
36999
|
-
this.watermarkObserver = new MutationObserver(() => {
|
|
37000
|
-
if (this.container && this.watermarkEl && !this.container.contains(this.watermarkEl)) {
|
|
37001
|
-
this.container.appendChild(this.watermarkEl);
|
|
37002
|
-
}
|
|
37003
|
-
});
|
|
37004
|
-
this.watermarkObserver.observe(this.container, { childList: true, subtree: false });
|
|
37005
|
-
}
|
|
37006
|
-
handleKeyDown(e) {
|
|
37007
|
-
if (e.key === 'F12') {
|
|
37008
|
-
e.preventDefault();
|
|
37009
|
-
return;
|
|
37010
|
-
}
|
|
37011
|
-
if (e.ctrlKey && e.shiftKey && ['I', 'J', 'C'].includes(e.key.toUpperCase())) {
|
|
37012
|
-
e.preventDefault();
|
|
37013
|
-
return;
|
|
37014
|
-
}
|
|
37015
|
-
if (e.ctrlKey && e.key.toUpperCase() === 'U') {
|
|
37016
|
-
e.preventDefault();
|
|
37017
|
-
}
|
|
37018
36738
|
}
|
|
37019
36739
|
enforceMaxRate() {
|
|
37020
36740
|
const max = this.config.security.maxPlaybackRate;
|
|
37021
36741
|
if (this.videoElement.playbackRate > max) {
|
|
37022
36742
|
this.videoElement.playbackRate = max;
|
|
37023
|
-
this.log(
|
|
36743
|
+
this.log('Playback rate clamped to ' + max);
|
|
37024
36744
|
}
|
|
37025
36745
|
}
|
|
37026
36746
|
async initialize() {
|
|
@@ -37045,38 +36765,38 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37045
36765
|
return;
|
|
37046
36766
|
try {
|
|
37047
36767
|
const tokenId = this.embedToken.tokenId;
|
|
37048
|
-
const url =
|
|
36768
|
+
const url = this.config.apiBaseUrl +
|
|
36769
|
+
'/videos/stream/' + this.videoId +
|
|
36770
|
+
'/viewer-config?token=' + encodeURIComponent(tokenId);
|
|
37049
36771
|
const resp = await fetch(url, { credentials: 'omit' });
|
|
37050
36772
|
if (!resp.ok) {
|
|
37051
36773
|
this.log('viewer-config fetch failed, falling back to SDK config', resp.status);
|
|
37052
36774
|
const sec = this.config.security;
|
|
37053
|
-
if (sec.enableWatermark && sec.watermarkText
|
|
37054
|
-
this.
|
|
36775
|
+
if (sec.enableWatermark && sec.watermarkText) {
|
|
36776
|
+
this.config.onWatermark?.(sec.watermarkText);
|
|
37055
36777
|
}
|
|
37056
36778
|
return;
|
|
37057
36779
|
}
|
|
37058
36780
|
const cfg = await resp.json();
|
|
37059
36781
|
this.log('Watermark config from server:', cfg);
|
|
37060
|
-
if (cfg.enableWatermark && cfg.watermarkText
|
|
37061
|
-
this.
|
|
36782
|
+
if (cfg.enableWatermark && cfg.watermarkText) {
|
|
36783
|
+
this.config.onWatermark?.(cfg.watermarkText);
|
|
37062
36784
|
}
|
|
37063
36785
|
}
|
|
37064
36786
|
catch (err) {
|
|
37065
36787
|
this.log('fetchAndApplyWatermark error (non-fatal):', err);
|
|
37066
36788
|
const sec = this.config.security;
|
|
37067
|
-
if (sec.enableWatermark && sec.watermarkText
|
|
37068
|
-
this.
|
|
36789
|
+
if (sec.enableWatermark && sec.watermarkText) {
|
|
36790
|
+
this.config.onWatermark?.(sec.watermarkText);
|
|
37069
36791
|
}
|
|
37070
36792
|
}
|
|
37071
36793
|
}
|
|
37072
36794
|
async fetchEmbedToken() {
|
|
37073
|
-
const url =
|
|
36795
|
+
const url = this.config.embedTokenEndpoint + '/' + this.videoId;
|
|
37074
36796
|
this.log('Fetching embed token from', url);
|
|
37075
36797
|
const response = await fetch(url, {
|
|
37076
36798
|
method: 'POST',
|
|
37077
|
-
headers: {
|
|
37078
|
-
'Content-Type': 'application/json',
|
|
37079
|
-
},
|
|
36799
|
+
headers: { 'Content-Type': 'application/json' },
|
|
37080
36800
|
body: JSON.stringify({
|
|
37081
36801
|
allowedDomain: window.location.origin,
|
|
37082
36802
|
expiresInMinutes: 120,
|
|
@@ -37128,14 +36848,11 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37128
36848
|
this.hls.loadSource(playerUrl);
|
|
37129
36849
|
this.hls.attachMedia(this.videoElement);
|
|
37130
36850
|
this.hls.on(Hls.Events.MANIFEST_PARSED, (_event, data) => {
|
|
37131
|
-
this.log('HLS manifest parsed', {
|
|
37132
|
-
levels: data.levels.map((l) => `${l.height}p`),
|
|
37133
|
-
});
|
|
36851
|
+
this.log('HLS manifest parsed', { levels: data.levels.map((l) => l.height + 'p') });
|
|
37134
36852
|
this.setState(exports.PlayerState.READY);
|
|
37135
36853
|
this.config.onReady();
|
|
37136
|
-
if (this.config.autoplay)
|
|
36854
|
+
if (this.config.autoplay)
|
|
37137
36855
|
this.play();
|
|
37138
|
-
}
|
|
37139
36856
|
});
|
|
37140
36857
|
this.hls.on(Hls.Events.LEVEL_SWITCHED, (_event, data) => {
|
|
37141
36858
|
const level = this.hls.levels[data.level];
|
|
@@ -37144,10 +36861,10 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37144
36861
|
height: level.height,
|
|
37145
36862
|
width: level.width,
|
|
37146
36863
|
bitrate: level.bitrate,
|
|
37147
|
-
name:
|
|
36864
|
+
name: level.height + 'p',
|
|
37148
36865
|
};
|
|
37149
36866
|
this.currentQuality = quality;
|
|
37150
|
-
this.log(
|
|
36867
|
+
this.log('Quality switched to ' + quality.name);
|
|
37151
36868
|
this.config.onQualityChange(quality.name);
|
|
37152
36869
|
});
|
|
37153
36870
|
this.hls.on(Hls.Events.ERROR, (_event, data) => {
|
|
@@ -37163,40 +36880,28 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37163
36880
|
this.hls?.recoverMediaError();
|
|
37164
36881
|
break;
|
|
37165
36882
|
default:
|
|
37166
|
-
this.handleError({
|
|
37167
|
-
code: data.type,
|
|
37168
|
-
message: data.details,
|
|
37169
|
-
fatal: true,
|
|
37170
|
-
details: data,
|
|
37171
|
-
});
|
|
37172
|
-
break;
|
|
36883
|
+
this.handleError({ code: data.type, message: data.details, fatal: true, details: data });
|
|
37173
36884
|
}
|
|
37174
36885
|
}
|
|
37175
36886
|
});
|
|
37176
36887
|
this.setupVideoEventListeners();
|
|
37177
36888
|
}
|
|
37178
36889
|
setupVideoEventListeners() {
|
|
37179
|
-
this.videoElement.addEventListener('playing', () =>
|
|
37180
|
-
|
|
37181
|
-
|
|
37182
|
-
this.videoElement.addEventListener('pause', () => {
|
|
37183
|
-
this.setState(exports.PlayerState.PAUSED);
|
|
37184
|
-
});
|
|
37185
|
-
this.videoElement.addEventListener('waiting', () => {
|
|
37186
|
-
this.setState(exports.PlayerState.BUFFERING);
|
|
37187
|
-
});
|
|
36890
|
+
this.videoElement.addEventListener('playing', () => this.setState(exports.PlayerState.PLAYING));
|
|
36891
|
+
this.videoElement.addEventListener('pause', () => this.setState(exports.PlayerState.PAUSED));
|
|
36892
|
+
this.videoElement.addEventListener('waiting', () => this.setState(exports.PlayerState.BUFFERING));
|
|
37188
36893
|
this.videoElement.addEventListener('error', () => {
|
|
37189
36894
|
const error = this.videoElement.error;
|
|
37190
36895
|
if (error) {
|
|
37191
|
-
const
|
|
36896
|
+
const msgs = {
|
|
37192
36897
|
1: 'Video loading aborted',
|
|
37193
36898
|
2: 'Network error',
|
|
37194
36899
|
3: 'Video decoding failed',
|
|
37195
36900
|
4: 'Video format not supported',
|
|
37196
36901
|
};
|
|
37197
36902
|
this.handleError({
|
|
37198
|
-
code:
|
|
37199
|
-
message:
|
|
36903
|
+
code: 'MEDIA_ERROR_' + error.code,
|
|
36904
|
+
message: msgs[error.code] || 'Unknown media error',
|
|
37200
36905
|
fatal: true,
|
|
37201
36906
|
details: error,
|
|
37202
36907
|
});
|
|
@@ -37217,21 +36922,11 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37217
36922
|
throw err;
|
|
37218
36923
|
}
|
|
37219
36924
|
}
|
|
37220
|
-
pause() {
|
|
37221
|
-
|
|
37222
|
-
}
|
|
37223
|
-
|
|
37224
|
-
|
|
37225
|
-
}
|
|
37226
|
-
seek(time) {
|
|
37227
|
-
this.videoElement.currentTime = time;
|
|
37228
|
-
}
|
|
37229
|
-
getDuration() {
|
|
37230
|
-
return this.videoElement.duration || 0;
|
|
37231
|
-
}
|
|
37232
|
-
getVolume() {
|
|
37233
|
-
return this.videoElement.volume;
|
|
37234
|
-
}
|
|
36925
|
+
pause() { this.videoElement.pause(); }
|
|
36926
|
+
getCurrentTime() { return this.videoElement.currentTime; }
|
|
36927
|
+
seek(time) { this.videoElement.currentTime = time; }
|
|
36928
|
+
getDuration() { return this.videoElement.duration || 0; }
|
|
36929
|
+
getVolume() { return this.videoElement.volume; }
|
|
37235
36930
|
setVolume(volume) {
|
|
37236
36931
|
this.videoElement.volume = Math.max(0, Math.min(1, volume));
|
|
37237
36932
|
}
|
|
@@ -37243,34 +36938,20 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37243
36938
|
height: level.height,
|
|
37244
36939
|
width: level.width,
|
|
37245
36940
|
bitrate: level.bitrate,
|
|
37246
|
-
name:
|
|
36941
|
+
name: level.height + 'p',
|
|
37247
36942
|
}));
|
|
37248
36943
|
}
|
|
37249
|
-
getCurrentQuality() {
|
|
37250
|
-
return this.currentQuality;
|
|
37251
|
-
}
|
|
36944
|
+
getCurrentQuality() { return this.currentQuality; }
|
|
37252
36945
|
setQuality(levelIndex) {
|
|
37253
36946
|
if (this.hls) {
|
|
37254
36947
|
this.hls.currentLevel = levelIndex;
|
|
37255
|
-
this.log(
|
|
36948
|
+
this.log('Quality set to level ' + levelIndex);
|
|
37256
36949
|
}
|
|
37257
36950
|
}
|
|
37258
|
-
getState() {
|
|
37259
|
-
return this.state;
|
|
37260
|
-
}
|
|
36951
|
+
getState() { return this.state; }
|
|
37261
36952
|
destroy() {
|
|
37262
36953
|
this.log('Destroying player');
|
|
37263
|
-
const target = this.container || this.videoElement;
|
|
37264
|
-
target.removeEventListener('contextmenu', this._onCtx);
|
|
37265
|
-
target.removeEventListener('selectstart', this._onSelectStart);
|
|
37266
|
-
this.videoElement.removeEventListener('dragstart', this._onDragStart);
|
|
37267
36954
|
this.videoElement.removeEventListener('ratechange', this._onRateChange);
|
|
37268
|
-
document.removeEventListener('click', this._onDocClick);
|
|
37269
|
-
document.removeEventListener('keydown', this._onKeyDown);
|
|
37270
|
-
this.hideContextMenu();
|
|
37271
|
-
this.watermarkObserver?.disconnect();
|
|
37272
|
-
this.watermarkEl?.remove();
|
|
37273
|
-
this.ctxStyleTag?.remove();
|
|
37274
36955
|
if (this.hls) {
|
|
37275
36956
|
this.hls.destroy();
|
|
37276
36957
|
this.hls = null;
|
|
@@ -37357,6 +37038,10 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37357
37038
|
-ms-user-select: none;
|
|
37358
37039
|
user-select: none;
|
|
37359
37040
|
outline: none;
|
|
37041
|
+
/* Reserve space at the bottom so the video is never covered by the controls bar.
|
|
37042
|
+
Controls bar ≈ 10px top padding + seek(~20px) + btn row(~34px) + 14px bottom = ~88px.
|
|
37043
|
+
We add this as padding-bottom so the video shrinks up rather than sitting behind the bar. */
|
|
37044
|
+
padding-bottom: 90px;
|
|
37360
37045
|
|
|
37361
37046
|
/* Subtle inner vignette for cinema depth */
|
|
37362
37047
|
box-shadow:
|
|
@@ -37670,6 +37355,7 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37670
37355
|
width: 0;
|
|
37671
37356
|
max-width: 72px;
|
|
37672
37357
|
height: 3px;
|
|
37358
|
+
/* Default background — overridden by JS inline style for the fill gradient */
|
|
37673
37359
|
background: rgba(255,255,255,0.18);
|
|
37674
37360
|
border-radius: 99px;
|
|
37675
37361
|
outline: none;
|
|
@@ -37687,6 +37373,12 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37687
37373
|
width: 72px;
|
|
37688
37374
|
opacity: 1;
|
|
37689
37375
|
}
|
|
37376
|
+
/* WebKit runnable track — gradient is set via inline style by JS */
|
|
37377
|
+
.gvp-volume-slider::-webkit-slider-runnable-track {
|
|
37378
|
+
height: 3px;
|
|
37379
|
+
border-radius: 99px;
|
|
37380
|
+
background: inherit; /* picks up the JS inline style gradient */
|
|
37381
|
+
}
|
|
37690
37382
|
/* WebKit thumb */
|
|
37691
37383
|
.gvp-volume-slider::-webkit-slider-thumb {
|
|
37692
37384
|
-webkit-appearance: none;
|
|
@@ -37694,9 +37386,16 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37694
37386
|
border-radius: 50%;
|
|
37695
37387
|
background: #fff;
|
|
37696
37388
|
cursor: pointer;
|
|
37389
|
+
margin-top: -4.5px; /* vertically centre over the 3px track */
|
|
37697
37390
|
-webkit-box-shadow: 0 1px 4px rgba(0,0,0,0.4);
|
|
37698
37391
|
box-shadow: 0 1px 4px rgba(0,0,0,0.4);
|
|
37699
37392
|
}
|
|
37393
|
+
/* Firefox — native filled track */
|
|
37394
|
+
.gvp-volume-slider::-moz-range-progress {
|
|
37395
|
+
background: var(--gvp-accent);
|
|
37396
|
+
border-radius: 99px;
|
|
37397
|
+
height: 3px;
|
|
37398
|
+
}
|
|
37700
37399
|
/* Firefox thumb */
|
|
37701
37400
|
.gvp-volume-slider::-moz-range-thumb {
|
|
37702
37401
|
width: 12px; height: 12px;
|
|
@@ -37970,6 +37669,81 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37970
37669
|
.gvp-time { font-size: 11px; }
|
|
37971
37670
|
.gvp-controls-inner { padding: 8px 10px; }
|
|
37972
37671
|
}
|
|
37672
|
+
|
|
37673
|
+
/* ── Branded context menu ─────────────────────────────────────── */
|
|
37674
|
+
.gvp-ctx-menu {
|
|
37675
|
+
position: fixed;
|
|
37676
|
+
z-index: 2147483647;
|
|
37677
|
+
min-width: 220px;
|
|
37678
|
+
background: rgba(12,12,18,0.96);
|
|
37679
|
+
-webkit-backdrop-filter: blur(16px) saturate(180%);
|
|
37680
|
+
backdrop-filter: blur(16px) saturate(180%);
|
|
37681
|
+
border: 1px solid rgba(255,255,255,0.08);
|
|
37682
|
+
border-radius: 12px;
|
|
37683
|
+
padding: 6px 0;
|
|
37684
|
+
-webkit-box-shadow: 0 12px 40px rgba(0,0,0,0.55), 0 0 0 0.5px rgba(255,255,255,0.04);
|
|
37685
|
+
box-shadow: 0 12px 40px rgba(0,0,0,0.55), 0 0 0 0.5px rgba(255,255,255,0.04);
|
|
37686
|
+
font-family: var(--gvp-font, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
|
|
37687
|
+
font-size: 13px;
|
|
37688
|
+
color: rgba(255,255,255,0.9);
|
|
37689
|
+
-webkit-user-select: none;
|
|
37690
|
+
-moz-user-select: none;
|
|
37691
|
+
-ms-user-select: none;
|
|
37692
|
+
user-select: none;
|
|
37693
|
+
-webkit-animation: gvp-ctx-in 0.12s cubic-bezier(0.22,1,0.36,1);
|
|
37694
|
+
animation: gvp-ctx-in 0.12s cubic-bezier(0.22,1,0.36,1);
|
|
37695
|
+
}
|
|
37696
|
+
@-webkit-keyframes gvp-ctx-in {
|
|
37697
|
+
from { opacity: 0; -webkit-transform: scale(0.95); transform: scale(0.95); }
|
|
37698
|
+
to { opacity: 1; -webkit-transform: none; transform: none; }
|
|
37699
|
+
}
|
|
37700
|
+
@keyframes gvp-ctx-in {
|
|
37701
|
+
from { opacity: 0; transform: scale(0.95); }
|
|
37702
|
+
to { opacity: 1; transform: none; }
|
|
37703
|
+
}
|
|
37704
|
+
.gvp-ctx-header {
|
|
37705
|
+
display: -webkit-box; display: -ms-flexbox; display: flex;
|
|
37706
|
+
-webkit-box-align: center; -ms-flex-align: center; align-items: center;
|
|
37707
|
+
gap: 8px;
|
|
37708
|
+
padding: 8px 14px 8px 12px;
|
|
37709
|
+
text-decoration: none;
|
|
37710
|
+
color: inherit;
|
|
37711
|
+
-webkit-transition: background 0.15s; transition: background 0.15s;
|
|
37712
|
+
border-radius: 8px 8px 0 0;
|
|
37713
|
+
}
|
|
37714
|
+
.gvp-ctx-header:hover { background: rgba(255,255,255,0.06); }
|
|
37715
|
+
.gvp-ctx-logo { border-radius: 4px; }
|
|
37716
|
+
.gvp-ctx-brand-name {
|
|
37717
|
+
font-weight: 600;
|
|
37718
|
+
color: var(--gvp-accent);
|
|
37719
|
+
white-space: nowrap;
|
|
37720
|
+
}
|
|
37721
|
+
.gvp-ctx-tag {
|
|
37722
|
+
margin-left: auto;
|
|
37723
|
+
font-size: 10px;
|
|
37724
|
+
color: rgba(255,255,255,0.3);
|
|
37725
|
+
white-space: nowrap;
|
|
37726
|
+
}
|
|
37727
|
+
.gvp-ctx-sep {
|
|
37728
|
+
height: 1px;
|
|
37729
|
+
margin: 4px 10px;
|
|
37730
|
+
background: rgba(255,255,255,0.07);
|
|
37731
|
+
}
|
|
37732
|
+
.gvp-ctx-item {
|
|
37733
|
+
display: -webkit-box; display: -ms-flexbox; display: flex;
|
|
37734
|
+
-webkit-box-align: center; -ms-flex-align: center; align-items: center;
|
|
37735
|
+
gap: 8px;
|
|
37736
|
+
padding: 7px 14px 7px 12px;
|
|
37737
|
+
cursor: pointer;
|
|
37738
|
+
-webkit-transition: background 0.15s; transition: background 0.15s;
|
|
37739
|
+
}
|
|
37740
|
+
.gvp-ctx-item:hover { background: rgba(255,255,255,0.06); }
|
|
37741
|
+
.gvp-ctx-item-icon { border-radius: 2px; }
|
|
37742
|
+
.gvp-ctx-version {
|
|
37743
|
+
padding: 4px 14px 6px 12px;
|
|
37744
|
+
font-size: 10px;
|
|
37745
|
+
color: rgba(255,255,255,0.25);
|
|
37746
|
+
}
|
|
37973
37747
|
`;
|
|
37974
37748
|
const tag = document.createElement('style');
|
|
37975
37749
|
tag.setAttribute('data-guardvideo', 'player-ui-styles-v2');
|
|
@@ -37987,6 +37761,12 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37987
37761
|
this.openMenu = null;
|
|
37988
37762
|
this.hideTimer = null;
|
|
37989
37763
|
this.seekDragging = false;
|
|
37764
|
+
this._ctxMenu = null;
|
|
37765
|
+
this._ctxDocClickBound = () => { };
|
|
37766
|
+
this._ctxTargetBound = () => { };
|
|
37767
|
+
this._ctxKeyDownBound = () => { };
|
|
37768
|
+
this._watermarkObserver = null;
|
|
37769
|
+
this._watermarkText = '';
|
|
37990
37770
|
const accent = config.branding?.accentColor ?? '#00e5a0';
|
|
37991
37771
|
const brandName = config.branding?.name ?? 'GuardVideo';
|
|
37992
37772
|
injectStyles();
|
|
@@ -38134,11 +37914,13 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
38134
37914
|
};
|
|
38135
37915
|
this._seekTouchEndBound = () => this._endSeekDrag();
|
|
38136
37916
|
this._wireEvents(videoId, config);
|
|
37917
|
+
requestAnimationFrame(() => this._updateVolSliderFill(1));
|
|
38137
37918
|
if (config.forensicWatermark !== false) {
|
|
38138
37919
|
const wmText = config.viewerEmail || config.viewerName || '';
|
|
38139
37920
|
if (wmText)
|
|
38140
37921
|
this._renderWatermark(wmText);
|
|
38141
37922
|
}
|
|
37923
|
+
this._applySecurity(config);
|
|
38142
37924
|
}
|
|
38143
37925
|
_hexToRgba(hex, alpha) {
|
|
38144
37926
|
const clean = hex.replace('#', '');
|
|
@@ -38187,6 +37969,7 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
38187
37969
|
this._onStateChange(state);
|
|
38188
37970
|
config.onStateChange?.(state);
|
|
38189
37971
|
},
|
|
37972
|
+
onWatermark: (text) => this._renderWatermark(text),
|
|
38190
37973
|
});
|
|
38191
37974
|
video.addEventListener('timeupdate', () => {
|
|
38192
37975
|
config.onTimeUpdate?.(video.currentTime);
|
|
@@ -38222,6 +38005,7 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
38222
38005
|
this.volSlider.addEventListener('input', () => {
|
|
38223
38006
|
video.volume = parseFloat(this.volSlider.value);
|
|
38224
38007
|
video.muted = video.volume === 0;
|
|
38008
|
+
this._updateVolSliderFill(video.volume);
|
|
38225
38009
|
});
|
|
38226
38010
|
this.seekWrap.addEventListener('mousedown', (e) => {
|
|
38227
38011
|
e.preventDefault();
|
|
@@ -38339,6 +38123,14 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
38339
38123
|
this.playBtn.title = `${label} (k)`;
|
|
38340
38124
|
}
|
|
38341
38125
|
_toggleMute() { this.videoEl.muted = !this.videoEl.muted; }
|
|
38126
|
+
_updateVolSliderFill(vol) {
|
|
38127
|
+
const accent = getComputedStyle(this.root).getPropertyValue('--gvp-accent').trim() || '#00e5a0';
|
|
38128
|
+
const pct = Math.round(vol * 100);
|
|
38129
|
+
this.volSlider.style.background =
|
|
38130
|
+
`linear-gradient(to right,` +
|
|
38131
|
+
` ${accent} 0%, ${accent} ${pct}%,` +
|
|
38132
|
+
` rgba(255,255,255,0.18) ${pct}%, rgba(255,255,255,0.18) 100%)`;
|
|
38133
|
+
}
|
|
38342
38134
|
_onVolumeChange() {
|
|
38343
38135
|
const v = this.videoEl;
|
|
38344
38136
|
const vol = v.muted ? 0 : v.volume;
|
|
@@ -38349,6 +38141,7 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
38349
38141
|
this.volBtn.setAttribute('aria-label', muted ? 'Unmute' : 'Mute');
|
|
38350
38142
|
this.volBtn.title = muted ? 'Unmute (m)' : 'Mute (m)';
|
|
38351
38143
|
this.volSlider.value = String(vol);
|
|
38144
|
+
this._updateVolSliderFill(vol);
|
|
38352
38145
|
}
|
|
38353
38146
|
_startSeekDrag() {
|
|
38354
38147
|
this.seekDragging = true;
|
|
@@ -38535,6 +38328,9 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
38535
38328
|
this.controls.classList.add('gvp-hidden');
|
|
38536
38329
|
}
|
|
38537
38330
|
_renderWatermark(text) {
|
|
38331
|
+
if (!text)
|
|
38332
|
+
return;
|
|
38333
|
+
this._watermarkText = text;
|
|
38538
38334
|
this.watermarkDiv.innerHTML = '';
|
|
38539
38335
|
for (let i = 0; i < 20; i++) {
|
|
38540
38336
|
const span = el('span', 'gvp-watermark-text');
|
|
@@ -38543,6 +38339,40 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
38543
38339
|
span.style.top = `${Math.floor(i / 4) * 22}%`;
|
|
38544
38340
|
this.watermarkDiv.appendChild(span);
|
|
38545
38341
|
}
|
|
38342
|
+
this._mountWatermarkObserver();
|
|
38343
|
+
}
|
|
38344
|
+
_mountWatermarkObserver() {
|
|
38345
|
+
this._watermarkObserver?.disconnect();
|
|
38346
|
+
this._watermarkObserver = new MutationObserver((mutations) => {
|
|
38347
|
+
for (const m of mutations) {
|
|
38348
|
+
if (m.type === 'childList' && m.target === this.root) {
|
|
38349
|
+
if (!this.root.contains(this.watermarkDiv)) {
|
|
38350
|
+
this.root.appendChild(this.watermarkDiv);
|
|
38351
|
+
}
|
|
38352
|
+
}
|
|
38353
|
+
if (m.type === 'attributes' && m.target === this.watermarkDiv) {
|
|
38354
|
+
this.watermarkDiv.removeAttribute('style');
|
|
38355
|
+
this.watermarkDiv.className = 'gvp-watermark';
|
|
38356
|
+
this.watermarkDiv.setAttribute('aria-hidden', 'true');
|
|
38357
|
+
}
|
|
38358
|
+
if (m.type === 'childList' && m.target === this.watermarkDiv) {
|
|
38359
|
+
if (this.watermarkDiv.childElementCount < 20) {
|
|
38360
|
+
this._refillWatermark();
|
|
38361
|
+
}
|
|
38362
|
+
}
|
|
38363
|
+
}
|
|
38364
|
+
});
|
|
38365
|
+
this._watermarkObserver.observe(this.root, { childList: true });
|
|
38366
|
+
this._watermarkObserver.observe(this.watermarkDiv, { attributes: true, childList: true });
|
|
38367
|
+
}
|
|
38368
|
+
_refillWatermark() {
|
|
38369
|
+
for (let i = this.watermarkDiv.childElementCount; i < 20; i++) {
|
|
38370
|
+
const span = el('span', 'gvp-watermark-text');
|
|
38371
|
+
span.textContent = this._watermarkText;
|
|
38372
|
+
span.style.left = `${(i % 4) * 26 + (Math.floor(i / 4) % 2) * 13}%`;
|
|
38373
|
+
span.style.top = `${Math.floor(i / 4) * 22}%`;
|
|
38374
|
+
this.watermarkDiv.appendChild(span);
|
|
38375
|
+
}
|
|
38546
38376
|
}
|
|
38547
38377
|
_addRipple(e) {
|
|
38548
38378
|
const rect = this.root.getBoundingClientRect();
|
|
@@ -38590,6 +38420,120 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
38590
38420
|
break;
|
|
38591
38421
|
}
|
|
38592
38422
|
}
|
|
38423
|
+
_applySecurity(config) {
|
|
38424
|
+
const sec = config.security;
|
|
38425
|
+
this.root.style.userSelect = 'none';
|
|
38426
|
+
this.root.style.webkitUserSelect = 'none';
|
|
38427
|
+
this.root.style.msUserSelect = 'none';
|
|
38428
|
+
if (sec?.disableDrag !== false) {
|
|
38429
|
+
this.videoEl.draggable = false;
|
|
38430
|
+
this.videoEl.addEventListener('dragstart', (e) => e.preventDefault());
|
|
38431
|
+
}
|
|
38432
|
+
if (sec?.blockDevTools) {
|
|
38433
|
+
this._ctxKeyDownBound = (e) => {
|
|
38434
|
+
if (e.key === 'F12') {
|
|
38435
|
+
e.preventDefault();
|
|
38436
|
+
return;
|
|
38437
|
+
}
|
|
38438
|
+
if (e.ctrlKey && e.shiftKey && ['I', 'J', 'C'].includes(e.key.toUpperCase())) {
|
|
38439
|
+
e.preventDefault();
|
|
38440
|
+
return;
|
|
38441
|
+
}
|
|
38442
|
+
if (e.ctrlKey && e.key.toUpperCase() === 'U')
|
|
38443
|
+
e.preventDefault();
|
|
38444
|
+
};
|
|
38445
|
+
document.addEventListener('keydown', this._ctxKeyDownBound);
|
|
38446
|
+
}
|
|
38447
|
+
this._ctxDocClickBound = () => this._hideContextMenu();
|
|
38448
|
+
document.addEventListener('click', this._ctxDocClickBound);
|
|
38449
|
+
this._ctxTargetBound = (e) => {
|
|
38450
|
+
e.preventDefault();
|
|
38451
|
+
e.stopPropagation();
|
|
38452
|
+
if (sec?.disableRightClick)
|
|
38453
|
+
return;
|
|
38454
|
+
const me = e;
|
|
38455
|
+
this._showContextMenu(me.clientX, me.clientY, config);
|
|
38456
|
+
};
|
|
38457
|
+
this.root.addEventListener('contextmenu', this._ctxTargetBound);
|
|
38458
|
+
}
|
|
38459
|
+
_showContextMenu(x, y, config) {
|
|
38460
|
+
this._hideContextMenu();
|
|
38461
|
+
const br = config.branding;
|
|
38462
|
+
const name = br?.name ?? 'GuardVideo';
|
|
38463
|
+
const url = br?.url ?? 'https://guardvid.com';
|
|
38464
|
+
const logoUrl = br?.logoUrl ?? '';
|
|
38465
|
+
const accent = br?.accentColor ?? '#00e5a0';
|
|
38466
|
+
const extras = config.contextMenuItems ?? [];
|
|
38467
|
+
const menu = el('div', 'gvp-ctx-menu');
|
|
38468
|
+
menu.setAttribute('role', 'menu');
|
|
38469
|
+
menu.style.setProperty('--gvp-accent', accent);
|
|
38470
|
+
const header = document.createElement('a');
|
|
38471
|
+
header.className = 'gvp-ctx-header';
|
|
38472
|
+
header.href = url;
|
|
38473
|
+
header.target = '_blank';
|
|
38474
|
+
header.rel = 'noopener noreferrer';
|
|
38475
|
+
header.setAttribute('role', 'menuitem');
|
|
38476
|
+
if (logoUrl) {
|
|
38477
|
+
const logo = el('img', 'gvp-ctx-logo');
|
|
38478
|
+
logo.src = logoUrl;
|
|
38479
|
+
logo.alt = name;
|
|
38480
|
+
logo.width = 20;
|
|
38481
|
+
logo.height = 20;
|
|
38482
|
+
header.appendChild(logo);
|
|
38483
|
+
}
|
|
38484
|
+
else {
|
|
38485
|
+
header.appendChild(svgEl(ICON.shield, 18, 18));
|
|
38486
|
+
}
|
|
38487
|
+
const nameSpan = el('span', 'gvp-ctx-brand-name');
|
|
38488
|
+
nameSpan.textContent = name;
|
|
38489
|
+
const tagSpan = el('span', 'gvp-ctx-tag');
|
|
38490
|
+
tagSpan.textContent = 'Secure Video Player';
|
|
38491
|
+
header.append(nameSpan, tagSpan);
|
|
38492
|
+
menu.appendChild(header);
|
|
38493
|
+
extras.forEach((item) => {
|
|
38494
|
+
if (item.separator) {
|
|
38495
|
+
menu.appendChild(el('div', 'gvp-ctx-sep'));
|
|
38496
|
+
return;
|
|
38497
|
+
}
|
|
38498
|
+
const row = el('div', 'gvp-ctx-item');
|
|
38499
|
+
row.setAttribute('role', 'menuitem');
|
|
38500
|
+
row.setAttribute('tabindex', '0');
|
|
38501
|
+
if (item.icon) {
|
|
38502
|
+
const ico = el('img', 'gvp-ctx-item-icon');
|
|
38503
|
+
ico.src = item.icon;
|
|
38504
|
+
ico.width = 14;
|
|
38505
|
+
ico.height = 14;
|
|
38506
|
+
row.appendChild(ico);
|
|
38507
|
+
}
|
|
38508
|
+
const lbl = el('span');
|
|
38509
|
+
lbl.textContent = item.label;
|
|
38510
|
+
row.appendChild(lbl);
|
|
38511
|
+
row.addEventListener('click', (ev) => {
|
|
38512
|
+
ev.stopPropagation();
|
|
38513
|
+
this._hideContextMenu();
|
|
38514
|
+
if (item.onClick)
|
|
38515
|
+
item.onClick();
|
|
38516
|
+
else if (item.href)
|
|
38517
|
+
window.open(item.href, '_blank', 'noopener,noreferrer');
|
|
38518
|
+
});
|
|
38519
|
+
menu.appendChild(row);
|
|
38520
|
+
});
|
|
38521
|
+
menu.appendChild(el('div', 'gvp-ctx-sep'));
|
|
38522
|
+
const ver = el('div', 'gvp-ctx-version');
|
|
38523
|
+
ver.textContent = `${name} Player v1.0`;
|
|
38524
|
+
menu.appendChild(ver);
|
|
38525
|
+
document.body.appendChild(menu);
|
|
38526
|
+
const rect = menu.getBoundingClientRect();
|
|
38527
|
+
menu.style.left = `${x + rect.width > window.innerWidth ? window.innerWidth - rect.width - 8 : x}px`;
|
|
38528
|
+
menu.style.top = `${y + rect.height > window.innerHeight ? window.innerHeight - rect.height - 8 : y}px`;
|
|
38529
|
+
this._ctxMenu = menu;
|
|
38530
|
+
}
|
|
38531
|
+
_hideContextMenu() {
|
|
38532
|
+
if (this._ctxMenu) {
|
|
38533
|
+
this._ctxMenu.remove();
|
|
38534
|
+
this._ctxMenu = null;
|
|
38535
|
+
}
|
|
38536
|
+
}
|
|
38593
38537
|
play() { return this.corePlayer.play(); }
|
|
38594
38538
|
pause() { return this.corePlayer.pause(); }
|
|
38595
38539
|
seek(t) { return this.corePlayer.seek(t); }
|
|
@@ -38613,6 +38557,10 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
38613
38557
|
window.removeEventListener('mouseup', this._seekMouseUpBound);
|
|
38614
38558
|
window.removeEventListener('touchmove', this._seekTouchMoveBound);
|
|
38615
38559
|
window.removeEventListener('touchend', this._seekTouchEndBound);
|
|
38560
|
+
document.removeEventListener('click', this._ctxDocClickBound);
|
|
38561
|
+
document.removeEventListener('keydown', this._ctxKeyDownBound);
|
|
38562
|
+
this._hideContextMenu();
|
|
38563
|
+
this._watermarkObserver?.disconnect();
|
|
38616
38564
|
this.corePlayer.destroy();
|
|
38617
38565
|
this.root.remove();
|
|
38618
38566
|
}
|