myetv-player 1.0.8 → 1.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/README.md +76 -2
- package/css/myetv-player.css +321 -208
- package/css/myetv-player.min.css +1 -1
- package/dist/myetv-player.js +219 -37
- package/dist/myetv-player.min.js +204 -26
- package/package.json +3 -1
- package/plugins/cloudflare/README.md +26 -4
- package/plugins/cloudflare/myetv-player-cloudflare-stream-plugin.js +1273 -217
- package/plugins/facebook/myetv-player-facebook-plugin.js +1340 -164
- package/plugins/twitch/myetv-player-twitch-plugin.js +428 -167
- package/plugins/vimeo/README.md +1 -1
- package/plugins/vimeo/myetv-player-vimeo.js +560 -247
- package/plugins/youtube/README.md +18 -7
- package/plugins/youtube/myetv-player-youtube-plugin.js +1485 -190
- package/scss/_base.scss +0 -15
- package/scss/_controls.scss +182 -2
- package/scss/_menus.scss +51 -0
- package/scss/_responsive.scss +187 -321
- package/scss/_title-overlay.scss +27 -0
- package/scss/_video.scss +0 -75
- package/scss/_watermark.scss +120 -0
- package/scss/myetv-player.scss +7 -7
- package/src/controls.js +72 -21
- package/src/core.js +43 -5
- package/src/events.js +33 -5
- package/src/utils.js +20 -6
- package/src/watermark.js +51 -0
package/dist/myetv-player.js
CHANGED
|
@@ -422,6 +422,7 @@ constructor(videoElement, options = {}) {
|
|
|
422
422
|
showSeekTooltip: true,
|
|
423
423
|
showTitleOverlay: false,
|
|
424
424
|
videoTitle: '',
|
|
425
|
+
videoSubtitle: '',
|
|
425
426
|
persistentTitle: false,
|
|
426
427
|
debug: false, // Enable/disable debug logging
|
|
427
428
|
autoplay: false, // if video should autoplay at start
|
|
@@ -978,9 +979,16 @@ createTitleOverlay() {
|
|
|
978
979
|
const titleText = document.createElement('h2');
|
|
979
980
|
titleText.className = 'title-text';
|
|
980
981
|
titleText.textContent = this.options.videoTitle || '';
|
|
981
|
-
|
|
982
982
|
overlay.appendChild(titleText);
|
|
983
983
|
|
|
984
|
+
// add subtitles
|
|
985
|
+
if (this.options.videoSubtitle) {
|
|
986
|
+
const subtitleText = document.createElement('p');
|
|
987
|
+
subtitleText.className = 'subtitle-text';
|
|
988
|
+
subtitleText.textContent = this.options.videoSubtitle;
|
|
989
|
+
overlay.appendChild(subtitleText);
|
|
990
|
+
}
|
|
991
|
+
|
|
984
992
|
if (this.controls) {
|
|
985
993
|
this.container.insertBefore(overlay, this.controls);
|
|
986
994
|
} else {
|
|
@@ -1054,6 +1062,31 @@ getVideoTitle() {
|
|
|
1054
1062
|
return this.options.videoTitle;
|
|
1055
1063
|
}
|
|
1056
1064
|
|
|
1065
|
+
setVideoSubtitle(subtitle) {
|
|
1066
|
+
this.options.videoSubtitle = subtitle || '';
|
|
1067
|
+
|
|
1068
|
+
if (this.titleOverlay) {
|
|
1069
|
+
let subtitleElement = this.titleOverlay.querySelector('.subtitle-text');
|
|
1070
|
+
|
|
1071
|
+
if (subtitle) {
|
|
1072
|
+
if (!subtitleElement) {
|
|
1073
|
+
subtitleElement = document.createElement('p');
|
|
1074
|
+
subtitleElement.className = 'subtitle-text';
|
|
1075
|
+
this.titleOverlay.appendChild(subtitleElement);
|
|
1076
|
+
}
|
|
1077
|
+
subtitleElement.textContent = subtitle;
|
|
1078
|
+
} else if (subtitleElement) {
|
|
1079
|
+
subtitleElement.remove();
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
return this;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
getVideoSubtitle() {
|
|
1087
|
+
return this.options.videoSubtitle;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1057
1090
|
setPersistentTitle(persistent) {
|
|
1058
1091
|
this.options.persistentTitle = persistent;
|
|
1059
1092
|
|
|
@@ -1533,14 +1566,19 @@ seek(e) {
|
|
|
1533
1566
|
if (!this.video || !this.progressContainer || !this.progressFilled || !this.progressHandle || this.isChangingQuality) return;
|
|
1534
1567
|
|
|
1535
1568
|
const rect = this.progressContainer.getBoundingClientRect();
|
|
1536
|
-
|
|
1569
|
+
|
|
1570
|
+
// Support both mouse and touch events
|
|
1571
|
+
const clientX = e.clientX !== undefined ? e.clientX : (e.touches && e.touches[0] ? e.touches[0].clientX : (e.changedTouches && e.changedTouches[0] ? e.changedTouches[0].clientX : 0));
|
|
1572
|
+
|
|
1573
|
+
const clickX = clientX - rect.left;
|
|
1537
1574
|
const percentage = Math.max(0, Math.min(1, clickX / rect.width));
|
|
1538
1575
|
|
|
1539
1576
|
if (this.video.duration && !isNaN(this.video.duration)) {
|
|
1540
1577
|
this.video.currentTime = percentage * this.video.duration;
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
this.
|
|
1578
|
+
|
|
1579
|
+
const progress = `${percentage * 100}%`;
|
|
1580
|
+
this.progressFilled.style.width = progress;
|
|
1581
|
+
this.progressHandle.style.left = progress;
|
|
1544
1582
|
}
|
|
1545
1583
|
}
|
|
1546
1584
|
|
|
@@ -2514,12 +2552,28 @@ addEventListener(eventType, callback) {
|
|
|
2514
2552
|
});
|
|
2515
2553
|
}
|
|
2516
2554
|
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2555
|
+
if (this.progressContainer) {
|
|
2556
|
+
// Mouse events (desktop)
|
|
2557
|
+
this.progressContainer.addEventListener('click', (e) => this.seek(e));
|
|
2558
|
+
this.progressContainer.addEventListener('mousedown', (e) => this.startSeeking(e));
|
|
2559
|
+
|
|
2560
|
+
// Touch events (mobile)
|
|
2561
|
+
this.progressContainer.addEventListener('touchstart', (e) => {
|
|
2562
|
+
e.preventDefault(); // Prevent scrolling when touching the seek bar
|
|
2563
|
+
this.startSeeking(e);
|
|
2564
|
+
}, { passive: false });
|
|
2521
2565
|
|
|
2522
2566
|
this.setupSeekTooltip();
|
|
2567
|
+
}
|
|
2568
|
+
|
|
2569
|
+
// Add touch events directly on the handle for better mobile dragging
|
|
2570
|
+
if (this.progressHandle) {
|
|
2571
|
+
this.progressHandle.addEventListener('touchstart', (e) => {
|
|
2572
|
+
e.preventDefault(); // Prevent default touch behavior
|
|
2573
|
+
e.stopPropagation(); // Stop event from bubbling to progressContainer
|
|
2574
|
+
this.startSeeking(e);
|
|
2575
|
+
}, { passive: false });
|
|
2576
|
+
}
|
|
2523
2577
|
|
|
2524
2578
|
// NOTE: Auto-hide events are handled in initAutoHide() after everything is ready
|
|
2525
2579
|
|
|
@@ -2540,7 +2594,19 @@ addEventListener(eventType, callback) {
|
|
|
2540
2594
|
document.addEventListener('mozfullscreenchange', () => this.updateFullscreenButton());
|
|
2541
2595
|
|
|
2542
2596
|
document.addEventListener('mousemove', (e) => this.continueSeeking(e));
|
|
2543
|
-
|
|
2597
|
+
document.addEventListener('mouseup', () => this.endSeeking());
|
|
2598
|
+
|
|
2599
|
+
// Touch events for seeking (mobile)
|
|
2600
|
+
document.addEventListener('touchmove', (e) => {
|
|
2601
|
+
if (this.isUserSeeking) {
|
|
2602
|
+
e.preventDefault(); // Prevent scrolling while seeking
|
|
2603
|
+
this.continueSeeking(e);
|
|
2604
|
+
}
|
|
2605
|
+
}, { passive: false });
|
|
2606
|
+
|
|
2607
|
+
document.addEventListener('touchend', () => this.endSeeking());
|
|
2608
|
+
document.addEventListener('touchcancel', () => this.endSeeking());
|
|
2609
|
+
|
|
2544
2610
|
}
|
|
2545
2611
|
|
|
2546
2612
|
/* Controls Module for MYETV Video Player
|
|
@@ -2548,7 +2614,7 @@ addEventListener(eventType, callback) {
|
|
|
2548
2614
|
* Created by https://www.myetv.tv https://oskarcosimo.com
|
|
2549
2615
|
*/
|
|
2550
2616
|
|
|
2551
|
-
/* AUTO-HIDE SYSTEM
|
|
2617
|
+
/* AUTO-HIDE SYSTEM */
|
|
2552
2618
|
initAutoHide() {
|
|
2553
2619
|
if (!this.options.autoHide) {
|
|
2554
2620
|
if (this.options.debug) console.log('Auto-hide disabled in options');
|
|
@@ -2688,12 +2754,17 @@ showControlsNow() {
|
|
|
2688
2754
|
this.controls.classList.add('show');
|
|
2689
2755
|
}
|
|
2690
2756
|
|
|
2691
|
-
//
|
|
2757
|
+
// Add has-controls class to container for watermark visibility
|
|
2692
2758
|
if (this.container) {
|
|
2693
2759
|
this.container.classList.add('has-controls');
|
|
2760
|
+
this.updateControlbarHeight();
|
|
2761
|
+
// Update watermark position
|
|
2762
|
+
if (this.updateWatermarkPosition) {
|
|
2763
|
+
this.updateWatermarkPosition();
|
|
2764
|
+
}
|
|
2694
2765
|
}
|
|
2695
2766
|
|
|
2696
|
-
//
|
|
2767
|
+
// Show title overlay with controls if not persistent
|
|
2697
2768
|
if (this.options.showTitleOverlay && !this.options.persistentTitle && this.options.videoTitle) {
|
|
2698
2769
|
this.showTitleOverlay();
|
|
2699
2770
|
}
|
|
@@ -2704,32 +2775,44 @@ showControlsNow() {
|
|
|
2704
2775
|
hideControlsNow() {
|
|
2705
2776
|
// Don't hide if mouse is still over controls (allow hiding on touch devices)
|
|
2706
2777
|
const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
|
2778
|
+
|
|
2707
2779
|
if (this.mouseOverControls && !isTouchDevice) {
|
|
2708
|
-
if (this.autoHideDebug && this.options.debug)
|
|
2780
|
+
if (this.autoHideDebug && this.options.debug) {
|
|
2781
|
+
console.log('🚫 Not hiding - mouse still over controls');
|
|
2782
|
+
}
|
|
2709
2783
|
return;
|
|
2710
2784
|
}
|
|
2711
2785
|
|
|
2712
2786
|
// Don't hide if video is paused
|
|
2713
2787
|
if (this.video && this.video.paused) {
|
|
2714
|
-
if (this.autoHideDebug && this.options.debug)
|
|
2788
|
+
if (this.autoHideDebug && this.options.debug) {
|
|
2789
|
+
console.log('🚫 Not hiding - video is paused');
|
|
2790
|
+
}
|
|
2715
2791
|
return;
|
|
2716
2792
|
}
|
|
2717
2793
|
|
|
2718
2794
|
if (this.controls) {
|
|
2719
2795
|
this.controls.classList.remove('show');
|
|
2720
|
-
}
|
|
2721
2796
|
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2797
|
+
// Remove has-controls class from container for watermark visibility
|
|
2798
|
+
if (this.container) {
|
|
2799
|
+
this.container.classList.remove('has-controls');
|
|
2800
|
+
this.updateControlbarHeight();
|
|
2801
|
+
// Update watermark position
|
|
2802
|
+
if (this.updateWatermarkPosition) {
|
|
2803
|
+
this.updateWatermarkPosition();
|
|
2804
|
+
}
|
|
2805
|
+
}
|
|
2725
2806
|
}
|
|
2726
2807
|
|
|
2727
|
-
//
|
|
2808
|
+
// Hide title overlay with controls (if not persistent)
|
|
2728
2809
|
if (this.options.showTitleOverlay && !this.options.persistentTitle) {
|
|
2729
2810
|
this.hideTitleOverlay();
|
|
2730
2811
|
}
|
|
2731
2812
|
|
|
2732
|
-
if (this.autoHideDebug && this.options.debug)
|
|
2813
|
+
if (this.autoHideDebug && this.options.debug) {
|
|
2814
|
+
console.log('👁️ Controls hidden');
|
|
2815
|
+
}
|
|
2733
2816
|
}
|
|
2734
2817
|
|
|
2735
2818
|
showControls() {
|
|
@@ -3008,28 +3091,62 @@ createControls() {
|
|
|
3008
3091
|
// NEW: Initialize responsive settings menu
|
|
3009
3092
|
setTimeout(() => {
|
|
3010
3093
|
this.initializeResponsiveMenu();
|
|
3094
|
+
this.updateControlbarHeight();
|
|
3011
3095
|
}, 100);
|
|
3012
3096
|
}
|
|
3013
3097
|
|
|
3014
|
-
/*
|
|
3098
|
+
/* Initialize responsive menu with dynamic width calculation */
|
|
3015
3099
|
initializeResponsiveMenu() {
|
|
3016
3100
|
if (!this.controls) return;
|
|
3017
3101
|
|
|
3018
3102
|
// Track screen size
|
|
3019
3103
|
this.isSmallScreen = false;
|
|
3020
3104
|
|
|
3021
|
-
// Check initial size
|
|
3105
|
+
// Check initial size
|
|
3022
3106
|
this.checkScreenSize();
|
|
3023
3107
|
|
|
3024
|
-
|
|
3108
|
+
// Bind resize handler with updateControlbarHeight
|
|
3109
|
+
const resizeHandler = () => {
|
|
3025
3110
|
this.checkScreenSize();
|
|
3026
|
-
|
|
3111
|
+
this.updateControlbarHeight();
|
|
3112
|
+
};
|
|
3113
|
+
|
|
3114
|
+
// Bind del context
|
|
3115
|
+
this.resizeHandler = resizeHandler.bind(this);
|
|
3116
|
+
window.addEventListener('resize', this.resizeHandler);
|
|
3027
3117
|
|
|
3028
3118
|
// Bind events for settings menu
|
|
3029
3119
|
this.bindSettingsMenuEvents();
|
|
3030
3120
|
}
|
|
3031
3121
|
|
|
3032
|
-
|
|
3122
|
+
// Dynamic controlbar height tracking for watermark positioning
|
|
3123
|
+
updateControlbarHeight() {
|
|
3124
|
+
if (!this.controls) return;
|
|
3125
|
+
|
|
3126
|
+
const height = this.controls.offsetHeight;
|
|
3127
|
+
if (this.container) {
|
|
3128
|
+
|
|
3129
|
+
this.container.style.setProperty('--player-controls-height', `${height}px`);
|
|
3130
|
+
|
|
3131
|
+
const watermark = this.container.querySelector('.video-watermark.watermark-bottomleft, .video-watermark.watermark-bottomright');
|
|
3132
|
+
if (watermark) {
|
|
3133
|
+
const hasControls = this.container.classList.contains('has-controls');
|
|
3134
|
+
const isHideOnAutoHide = watermark.classList.contains('hide-on-autohide');
|
|
3135
|
+
|
|
3136
|
+
if (hasControls || !isHideOnAutoHide) {
|
|
3137
|
+
watermark.style.bottom = `${height + 15}px`;
|
|
3138
|
+
} else {
|
|
3139
|
+
watermark.style.bottom = '15px';
|
|
3140
|
+
}
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
|
|
3144
|
+
if (this.options.debug) {
|
|
3145
|
+
console.log(`Controlbar height updated: ${height}px`);
|
|
3146
|
+
}
|
|
3147
|
+
}
|
|
3148
|
+
|
|
3149
|
+
/* Dynamic width calculation based on logo presence */
|
|
3033
3150
|
getResponsiveThreshold() {
|
|
3034
3151
|
// Check if brand logo is enabled and present
|
|
3035
3152
|
const hasLogo = this.options.brandLogoEnabled && this.options.brandLogoUrl;
|
|
@@ -3038,7 +3155,7 @@ getResponsiveThreshold() {
|
|
|
3038
3155
|
return hasLogo ? 650 : 550;
|
|
3039
3156
|
}
|
|
3040
3157
|
|
|
3041
|
-
/*
|
|
3158
|
+
/* Check if screen is under dynamic threshold */
|
|
3042
3159
|
checkScreenSize() {
|
|
3043
3160
|
const threshold = this.getResponsiveThreshold();
|
|
3044
3161
|
const newIsSmallScreen = window.innerWidth <= threshold;
|
|
@@ -3048,12 +3165,12 @@ checkScreenSize() {
|
|
|
3048
3165
|
this.updateSettingsMenuVisibility();
|
|
3049
3166
|
|
|
3050
3167
|
if (this.options.debug) {
|
|
3051
|
-
console.log(`Screen check: ${window.innerWidth}px vs ${threshold}px threshold
|
|
3168
|
+
console.log(`Screen check: ${window.innerWidth}px vs ${threshold}px (threshold), logo: ${this.options.brandLogoEnabled}, small: ${this.isSmallScreen}`);
|
|
3052
3169
|
}
|
|
3053
3170
|
}
|
|
3054
3171
|
}
|
|
3055
3172
|
|
|
3056
|
-
/*
|
|
3173
|
+
/* Update settings menu visibility based on screen size */
|
|
3057
3174
|
updateSettingsMenuVisibility() {
|
|
3058
3175
|
const settingsControl = this.controls?.querySelector('.settings-control');
|
|
3059
3176
|
if (!settingsControl) return;
|
|
@@ -3093,7 +3210,7 @@ updateSettingsMenuVisibility() {
|
|
|
3093
3210
|
}
|
|
3094
3211
|
}
|
|
3095
3212
|
|
|
3096
|
-
/*
|
|
3213
|
+
/* Populate settings menu with controls */
|
|
3097
3214
|
populateSettingsMenu() {
|
|
3098
3215
|
const settingsMenu = this.controls?.querySelector('.settings-menu');
|
|
3099
3216
|
if (!settingsMenu) return;
|
|
@@ -3155,7 +3272,7 @@ populateSettingsMenu() {
|
|
|
3155
3272
|
settingsMenu.innerHTML = menuHTML;
|
|
3156
3273
|
}
|
|
3157
3274
|
|
|
3158
|
-
/*
|
|
3275
|
+
/* Bind settings menu events */
|
|
3159
3276
|
bindSettingsMenuEvents() {
|
|
3160
3277
|
const settingsMenu = this.controls?.querySelector('.settings-menu');
|
|
3161
3278
|
if (!settingsMenu) return;
|
|
@@ -5784,9 +5901,19 @@ initializeWatermark() {
|
|
|
5784
5901
|
this.container.appendChild(watermark);
|
|
5785
5902
|
}
|
|
5786
5903
|
|
|
5904
|
+
// Store reference to watermark element
|
|
5787
5905
|
// Store reference to watermark element
|
|
5788
5906
|
this.watermarkElement = watermark;
|
|
5789
5907
|
|
|
5908
|
+
// Set initial position
|
|
5909
|
+
this.updateWatermarkPosition();
|
|
5910
|
+
|
|
5911
|
+
// Update position on window resize
|
|
5912
|
+
this.watermarkResizeHandler = () => {
|
|
5913
|
+
this.updateWatermarkPosition();
|
|
5914
|
+
};
|
|
5915
|
+
window.addEventListener('resize', this.watermarkResizeHandler);
|
|
5916
|
+
|
|
5790
5917
|
if (this.options.debug) {
|
|
5791
5918
|
console.log('🏷️ Watermark created:', {
|
|
5792
5919
|
url: this.options.watermarkUrl,
|
|
@@ -5805,6 +5932,7 @@ initializeWatermark() {
|
|
|
5805
5932
|
* @param {string} position - Position of watermark (topleft, topright, bottomleft, bottomright)
|
|
5806
5933
|
* @param {string} title - Optional tooltip title for the watermark
|
|
5807
5934
|
*/
|
|
5935
|
+
|
|
5808
5936
|
setWatermark(url, link = '', position = 'bottomright', title = '') {
|
|
5809
5937
|
// Update options
|
|
5810
5938
|
this.options.watermarkUrl = url;
|
|
@@ -5835,6 +5963,12 @@ removeWatermark() {
|
|
|
5835
5963
|
this.watermarkElement = null;
|
|
5836
5964
|
}
|
|
5837
5965
|
|
|
5966
|
+
// Remove resize listener
|
|
5967
|
+
if (this.watermarkResizeHandler) {
|
|
5968
|
+
window.removeEventListener('resize', this.watermarkResizeHandler);
|
|
5969
|
+
this.watermarkResizeHandler = null;
|
|
5970
|
+
}
|
|
5971
|
+
|
|
5838
5972
|
this.options.watermarkUrl = '';
|
|
5839
5973
|
this.options.watermarkLink = '';
|
|
5840
5974
|
this.options.watermarkPosition = 'bottomright';
|
|
@@ -5849,6 +5983,7 @@ removeWatermark() {
|
|
|
5849
5983
|
* Update watermark position
|
|
5850
5984
|
* @param {string} position - New position (topleft, topright, bottomleft, bottomright)
|
|
5851
5985
|
*/
|
|
5986
|
+
|
|
5852
5987
|
setWatermarkPosition(position) {
|
|
5853
5988
|
if (!['topleft', 'topright', 'bottomleft', 'bottomright'].includes(position)) {
|
|
5854
5989
|
if (this.options.debug) console.warn('🏷️ Invalid watermark position:', position);
|
|
@@ -5875,6 +6010,39 @@ setWatermarkPosition(position) {
|
|
|
5875
6010
|
return this;
|
|
5876
6011
|
}
|
|
5877
6012
|
|
|
6013
|
+
/**
|
|
6014
|
+
* Update watermark position based on current controlbar height
|
|
6015
|
+
* Called during window resize to keep watermark above controlbar
|
|
6016
|
+
*/
|
|
6017
|
+
updateWatermarkPosition() {
|
|
6018
|
+
if (!this.watermarkElement) return;
|
|
6019
|
+
if (!this.controls) return;
|
|
6020
|
+
|
|
6021
|
+
const position = this.options.watermarkPosition || 'bottomright';
|
|
6022
|
+
|
|
6023
|
+
// Only update bottom positions (top positions don't need adjustment)
|
|
6024
|
+
if (position === 'bottomleft' || position === 'bottomright') {
|
|
6025
|
+
const controlsHeight = this.controls.offsetHeight;
|
|
6026
|
+
const spacing = 15; // Same spacing used in CSS
|
|
6027
|
+
const bottomValue = controlsHeight + spacing;
|
|
6028
|
+
|
|
6029
|
+
// Check if controls are visible
|
|
6030
|
+
const hasControls = this.container.classList.contains('has-controls');
|
|
6031
|
+
|
|
6032
|
+
if (hasControls || !this.options.hideWatermark) {
|
|
6033
|
+
// Position above controlbar
|
|
6034
|
+
this.watermarkElement.style.bottom = `${bottomValue}px`;
|
|
6035
|
+
} else {
|
|
6036
|
+
// Position at bottom corner when controls hidden
|
|
6037
|
+
this.watermarkElement.style.bottom = '15px';
|
|
6038
|
+
}
|
|
6039
|
+
|
|
6040
|
+
if (this.options.debug) {
|
|
6041
|
+
console.log(`🏷️ Watermark position updated: bottom ${this.watermarkElement.style.bottom}`);
|
|
6042
|
+
}
|
|
6043
|
+
}
|
|
6044
|
+
}
|
|
6045
|
+
|
|
5878
6046
|
/**
|
|
5879
6047
|
* Set whether watermark should hide with controls
|
|
5880
6048
|
* @param {boolean} hide - True to hide watermark with controls, false to keep always visible
|
|
@@ -6631,15 +6799,29 @@ getBufferedTime() {
|
|
|
6631
6799
|
this.video.currentTime = Math.max(0, Math.min(this.video.duration, this.video.currentTime + seconds));
|
|
6632
6800
|
}
|
|
6633
6801
|
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6802
|
+
updateTimeDisplay() {
|
|
6803
|
+
// update current time
|
|
6804
|
+
if (this.currentTimeEl && this.video) {
|
|
6805
|
+
this.currentTimeEl.textContent = this.formatTime(this.video.currentTime || 0);
|
|
6806
|
+
}
|
|
6807
|
+
|
|
6808
|
+
// update duration or show badge if encoding
|
|
6809
|
+
if (this.durationEl && this.video) {
|
|
6810
|
+
const duration = this.video.duration;
|
|
6638
6811
|
|
|
6639
|
-
|
|
6640
|
-
|
|
6812
|
+
// check if duration is valid
|
|
6813
|
+
if (!duration || isNaN(duration) || !isFinite(duration)) {
|
|
6814
|
+
// Video in encoding - show badge instead of duration
|
|
6815
|
+
this.durationEl.innerHTML = '<span class="encoding-badge">Encoding in progress</span>';
|
|
6816
|
+
this.durationEl.classList.add('encoding-state');
|
|
6817
|
+
} else {
|
|
6818
|
+
// valid duration - show normal
|
|
6819
|
+
this.durationEl.textContent = this.formatTime(duration);
|
|
6820
|
+
this.durationEl.classList.remove('encoding-state');
|
|
6641
6821
|
}
|
|
6642
6822
|
}
|
|
6823
|
+
}
|
|
6824
|
+
|
|
6643
6825
|
|
|
6644
6826
|
formatTime(seconds) {
|
|
6645
6827
|
if (isNaN(seconds) || seconds < 0) return '0:00';
|