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.min.js
CHANGED
|
@@ -408,6 +408,7 @@ constructor(videoElement, options = {}) {
|
|
|
408
408
|
showSeekTooltip: true,
|
|
409
409
|
showTitleOverlay: false,
|
|
410
410
|
videoTitle: '',
|
|
411
|
+
videoSubtitle: '',
|
|
411
412
|
persistentTitle: false,
|
|
412
413
|
debug: false,
|
|
413
414
|
autoplay: false,
|
|
@@ -964,9 +965,16 @@ createTitleOverlay() {
|
|
|
964
965
|
const titleText = document.createElement('h2');
|
|
965
966
|
titleText.className = 'title-text';
|
|
966
967
|
titleText.textContent = this.options.videoTitle || '';
|
|
967
|
-
|
|
968
968
|
overlay.appendChild(titleText);
|
|
969
969
|
|
|
970
|
+
|
|
971
|
+
if (this.options.videoSubtitle) {
|
|
972
|
+
const subtitleText = document.createElement('p');
|
|
973
|
+
subtitleText.className = 'subtitle-text';
|
|
974
|
+
subtitleText.textContent = this.options.videoSubtitle;
|
|
975
|
+
overlay.appendChild(subtitleText);
|
|
976
|
+
}
|
|
977
|
+
|
|
970
978
|
if (this.controls) {
|
|
971
979
|
this.container.insertBefore(overlay, this.controls);
|
|
972
980
|
} else {
|
|
@@ -1040,6 +1048,31 @@ getVideoTitle() {
|
|
|
1040
1048
|
return this.options.videoTitle;
|
|
1041
1049
|
}
|
|
1042
1050
|
|
|
1051
|
+
setVideoSubtitle(subtitle) {
|
|
1052
|
+
this.options.videoSubtitle = subtitle || '';
|
|
1053
|
+
|
|
1054
|
+
if (this.titleOverlay) {
|
|
1055
|
+
let subtitleElement = this.titleOverlay.querySelector('.subtitle-text');
|
|
1056
|
+
|
|
1057
|
+
if (subtitle) {
|
|
1058
|
+
if (!subtitleElement) {
|
|
1059
|
+
subtitleElement = document.createElement('p');
|
|
1060
|
+
subtitleElement.className = 'subtitle-text';
|
|
1061
|
+
this.titleOverlay.appendChild(subtitleElement);
|
|
1062
|
+
}
|
|
1063
|
+
subtitleElement.textContent = subtitle;
|
|
1064
|
+
} else if (subtitleElement) {
|
|
1065
|
+
subtitleElement.remove();
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
return this;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
getVideoSubtitle() {
|
|
1073
|
+
return this.options.videoSubtitle;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1043
1076
|
setPersistentTitle(persistent) {
|
|
1044
1077
|
this.options.persistentTitle = persistent;
|
|
1045
1078
|
|
|
@@ -1510,14 +1543,19 @@ seek(e) {
|
|
|
1510
1543
|
if (!this.video || !this.progressContainer || !this.progressFilled || !this.progressHandle || this.isChangingQuality) return;
|
|
1511
1544
|
|
|
1512
1545
|
const rect = this.progressContainer.getBoundingClientRect();
|
|
1513
|
-
|
|
1546
|
+
|
|
1547
|
+
|
|
1548
|
+
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));
|
|
1549
|
+
|
|
1550
|
+
const clickX = clientX - rect.left;
|
|
1514
1551
|
const percentage = Math.max(0, Math.min(1, clickX / rect.width));
|
|
1515
1552
|
|
|
1516
1553
|
if (this.video.duration && !isNaN(this.video.duration)) {
|
|
1517
1554
|
this.video.currentTime = percentage * this.video.duration;
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
this.
|
|
1555
|
+
|
|
1556
|
+
const progress = `${percentage * 100}%`;
|
|
1557
|
+
this.progressFilled.style.width = progress;
|
|
1558
|
+
this.progressHandle.style.left = progress;
|
|
1521
1559
|
}
|
|
1522
1560
|
}
|
|
1523
1561
|
|
|
@@ -2429,12 +2467,28 @@ addEventListener(eventType, callback) {
|
|
|
2429
2467
|
});
|
|
2430
2468
|
}
|
|
2431
2469
|
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2470
|
+
if (this.progressContainer) {
|
|
2471
|
+
|
|
2472
|
+
this.progressContainer.addEventListener('click', (e) => this.seek(e));
|
|
2473
|
+
this.progressContainer.addEventListener('mousedown', (e) => this.startSeeking(e));
|
|
2474
|
+
|
|
2475
|
+
|
|
2476
|
+
this.progressContainer.addEventListener('touchstart', (e) => {
|
|
2477
|
+
e.preventDefault();
|
|
2478
|
+
this.startSeeking(e);
|
|
2479
|
+
}, { passive: false });
|
|
2436
2480
|
|
|
2437
2481
|
this.setupSeekTooltip();
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2484
|
+
|
|
2485
|
+
if (this.progressHandle) {
|
|
2486
|
+
this.progressHandle.addEventListener('touchstart', (e) => {
|
|
2487
|
+
e.preventDefault();
|
|
2488
|
+
e.stopPropagation();
|
|
2489
|
+
this.startSeeking(e);
|
|
2490
|
+
}, { passive: false });
|
|
2491
|
+
}
|
|
2438
2492
|
|
|
2439
2493
|
|
|
2440
2494
|
|
|
@@ -2455,7 +2509,19 @@ addEventListener(eventType, callback) {
|
|
|
2455
2509
|
document.addEventListener('mozfullscreenchange', () => this.updateFullscreenButton());
|
|
2456
2510
|
|
|
2457
2511
|
document.addEventListener('mousemove', (e) => this.continueSeeking(e));
|
|
2458
|
-
|
|
2512
|
+
document.addEventListener('mouseup', () => this.endSeeking());
|
|
2513
|
+
|
|
2514
|
+
|
|
2515
|
+
document.addEventListener('touchmove', (e) => {
|
|
2516
|
+
if (this.isUserSeeking) {
|
|
2517
|
+
e.preventDefault();
|
|
2518
|
+
this.continueSeeking(e);
|
|
2519
|
+
}
|
|
2520
|
+
}, { passive: false });
|
|
2521
|
+
|
|
2522
|
+
document.addEventListener('touchend', () => this.endSeeking());
|
|
2523
|
+
document.addEventListener('touchcancel', () => this.endSeeking());
|
|
2524
|
+
|
|
2459
2525
|
}
|
|
2460
2526
|
|
|
2461
2527
|
|
|
@@ -2603,6 +2669,11 @@ showControlsNow() {
|
|
|
2603
2669
|
|
|
2604
2670
|
if (this.container) {
|
|
2605
2671
|
this.container.classList.add('has-controls');
|
|
2672
|
+
this.updateControlbarHeight();
|
|
2673
|
+
|
|
2674
|
+
if (this.updateWatermarkPosition) {
|
|
2675
|
+
this.updateWatermarkPosition();
|
|
2676
|
+
}
|
|
2606
2677
|
}
|
|
2607
2678
|
|
|
2608
2679
|
|
|
@@ -2616,24 +2687,34 @@ showControlsNow() {
|
|
|
2616
2687
|
hideControlsNow() {
|
|
2617
2688
|
|
|
2618
2689
|
const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
|
2690
|
+
|
|
2619
2691
|
if (this.mouseOverControls && !isTouchDevice) {
|
|
2620
|
-
if (this.autoHideDebug && this.options.debug)
|
|
2692
|
+
if (this.autoHideDebug && this.options.debug) {
|
|
2693
|
+
console.log('🚫 Not hiding - mouse still over controls');
|
|
2694
|
+
}
|
|
2621
2695
|
return;
|
|
2622
2696
|
}
|
|
2623
2697
|
|
|
2624
2698
|
|
|
2625
2699
|
if (this.video && this.video.paused) {
|
|
2626
|
-
if (this.autoHideDebug && this.options.debug)
|
|
2700
|
+
if (this.autoHideDebug && this.options.debug) {
|
|
2701
|
+
console.log('🚫 Not hiding - video is paused');
|
|
2702
|
+
}
|
|
2627
2703
|
return;
|
|
2628
2704
|
}
|
|
2629
2705
|
|
|
2630
2706
|
if (this.controls) {
|
|
2631
2707
|
this.controls.classList.remove('show');
|
|
2632
|
-
}
|
|
2633
2708
|
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2709
|
+
|
|
2710
|
+
if (this.container) {
|
|
2711
|
+
this.container.classList.remove('has-controls');
|
|
2712
|
+
this.updateControlbarHeight();
|
|
2713
|
+
|
|
2714
|
+
if (this.updateWatermarkPosition) {
|
|
2715
|
+
this.updateWatermarkPosition();
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2637
2718
|
}
|
|
2638
2719
|
|
|
2639
2720
|
|
|
@@ -2641,7 +2722,9 @@ hideControlsNow() {
|
|
|
2641
2722
|
this.hideTitleOverlay();
|
|
2642
2723
|
}
|
|
2643
2724
|
|
|
2644
|
-
if (this.autoHideDebug && this.options.debug)
|
|
2725
|
+
if (this.autoHideDebug && this.options.debug) {
|
|
2726
|
+
console.log('👁️ Controls hidden');
|
|
2727
|
+
}
|
|
2645
2728
|
}
|
|
2646
2729
|
|
|
2647
2730
|
showControls() {
|
|
@@ -2919,6 +3002,7 @@ createControls() {
|
|
|
2919
3002
|
|
|
2920
3003
|
setTimeout(() => {
|
|
2921
3004
|
this.initializeResponsiveMenu();
|
|
3005
|
+
this.updateControlbarHeight();
|
|
2922
3006
|
}, 100);
|
|
2923
3007
|
}
|
|
2924
3008
|
|
|
@@ -2932,14 +3016,46 @@ initializeResponsiveMenu() {
|
|
|
2932
3016
|
|
|
2933
3017
|
this.checkScreenSize();
|
|
2934
3018
|
|
|
2935
|
-
|
|
3019
|
+
|
|
3020
|
+
const resizeHandler = () => {
|
|
2936
3021
|
this.checkScreenSize();
|
|
2937
|
-
|
|
3022
|
+
this.updateControlbarHeight();
|
|
3023
|
+
};
|
|
3024
|
+
|
|
3025
|
+
|
|
3026
|
+
this.resizeHandler = resizeHandler.bind(this);
|
|
3027
|
+
window.addEventListener('resize', this.resizeHandler);
|
|
2938
3028
|
|
|
2939
3029
|
|
|
2940
3030
|
this.bindSettingsMenuEvents();
|
|
2941
3031
|
}
|
|
2942
3032
|
|
|
3033
|
+
updateControlbarHeight() {
|
|
3034
|
+
if (!this.controls) return;
|
|
3035
|
+
|
|
3036
|
+
const height = this.controls.offsetHeight;
|
|
3037
|
+
if (this.container) {
|
|
3038
|
+
|
|
3039
|
+
this.container.style.setProperty('--player-controls-height', `${height}px`);
|
|
3040
|
+
|
|
3041
|
+
const watermark = this.container.querySelector('.video-watermark.watermark-bottomleft, .video-watermark.watermark-bottomright');
|
|
3042
|
+
if (watermark) {
|
|
3043
|
+
const hasControls = this.container.classList.contains('has-controls');
|
|
3044
|
+
const isHideOnAutoHide = watermark.classList.contains('hide-on-autohide');
|
|
3045
|
+
|
|
3046
|
+
if (hasControls || !isHideOnAutoHide) {
|
|
3047
|
+
watermark.style.bottom = `${height + 15}px`;
|
|
3048
|
+
} else {
|
|
3049
|
+
watermark.style.bottom = '15px';
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
}
|
|
3053
|
+
|
|
3054
|
+
if (this.options.debug) {
|
|
3055
|
+
console.log(`Controlbar height updated: ${height}px`);
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
|
|
2943
3059
|
|
|
2944
3060
|
getResponsiveThreshold() {
|
|
2945
3061
|
|
|
@@ -2959,7 +3075,7 @@ checkScreenSize() {
|
|
|
2959
3075
|
this.updateSettingsMenuVisibility();
|
|
2960
3076
|
|
|
2961
3077
|
if (this.options.debug) {
|
|
2962
|
-
console.log(`Screen check: ${window.innerWidth}px vs ${threshold}px threshold
|
|
3078
|
+
console.log(`Screen check: ${window.innerWidth}px vs ${threshold}px (threshold), logo: ${this.options.brandLogoEnabled}, small: ${this.isSmallScreen}`);
|
|
2963
3079
|
}
|
|
2964
3080
|
}
|
|
2965
3081
|
}
|
|
@@ -5621,8 +5737,18 @@ initializeWatermark() {
|
|
|
5621
5737
|
}
|
|
5622
5738
|
|
|
5623
5739
|
|
|
5740
|
+
|
|
5624
5741
|
this.watermarkElement = watermark;
|
|
5625
5742
|
|
|
5743
|
+
|
|
5744
|
+
this.updateWatermarkPosition();
|
|
5745
|
+
|
|
5746
|
+
|
|
5747
|
+
this.watermarkResizeHandler = () => {
|
|
5748
|
+
this.updateWatermarkPosition();
|
|
5749
|
+
};
|
|
5750
|
+
window.addEventListener('resize', this.watermarkResizeHandler);
|
|
5751
|
+
|
|
5626
5752
|
if (this.options.debug) {
|
|
5627
5753
|
console.log('🏷️ Watermark created:', {
|
|
5628
5754
|
url: this.options.watermarkUrl,
|
|
@@ -5635,6 +5761,7 @@ initializeWatermark() {
|
|
|
5635
5761
|
}
|
|
5636
5762
|
|
|
5637
5763
|
|
|
5764
|
+
|
|
5638
5765
|
setWatermark(url, link = '', position = 'bottomright', title = '') {
|
|
5639
5766
|
|
|
5640
5767
|
this.options.watermarkUrl = url;
|
|
@@ -5663,6 +5790,12 @@ removeWatermark() {
|
|
|
5663
5790
|
this.watermarkElement = null;
|
|
5664
5791
|
}
|
|
5665
5792
|
|
|
5793
|
+
|
|
5794
|
+
if (this.watermarkResizeHandler) {
|
|
5795
|
+
window.removeEventListener('resize', this.watermarkResizeHandler);
|
|
5796
|
+
this.watermarkResizeHandler = null;
|
|
5797
|
+
}
|
|
5798
|
+
|
|
5666
5799
|
this.options.watermarkUrl = '';
|
|
5667
5800
|
this.options.watermarkLink = '';
|
|
5668
5801
|
this.options.watermarkPosition = 'bottomright';
|
|
@@ -5674,6 +5807,7 @@ removeWatermark() {
|
|
|
5674
5807
|
}
|
|
5675
5808
|
|
|
5676
5809
|
|
|
5810
|
+
|
|
5677
5811
|
setWatermarkPosition(position) {
|
|
5678
5812
|
if (!['topleft', 'topright', 'bottomleft', 'bottomright'].includes(position)) {
|
|
5679
5813
|
if (this.options.debug) console.warn('🏷️ Invalid watermark position:', position);
|
|
@@ -5701,6 +5835,36 @@ setWatermarkPosition(position) {
|
|
|
5701
5835
|
}
|
|
5702
5836
|
|
|
5703
5837
|
|
|
5838
|
+
updateWatermarkPosition() {
|
|
5839
|
+
if (!this.watermarkElement) return;
|
|
5840
|
+
if (!this.controls) return;
|
|
5841
|
+
|
|
5842
|
+
const position = this.options.watermarkPosition || 'bottomright';
|
|
5843
|
+
|
|
5844
|
+
|
|
5845
|
+
if (position === 'bottomleft' || position === 'bottomright') {
|
|
5846
|
+
const controlsHeight = this.controls.offsetHeight;
|
|
5847
|
+
const spacing = 15;
|
|
5848
|
+
const bottomValue = controlsHeight + spacing;
|
|
5849
|
+
|
|
5850
|
+
|
|
5851
|
+
const hasControls = this.container.classList.contains('has-controls');
|
|
5852
|
+
|
|
5853
|
+
if (hasControls || !this.options.hideWatermark) {
|
|
5854
|
+
|
|
5855
|
+
this.watermarkElement.style.bottom = `${bottomValue}px`;
|
|
5856
|
+
} else {
|
|
5857
|
+
|
|
5858
|
+
this.watermarkElement.style.bottom = '15px';
|
|
5859
|
+
}
|
|
5860
|
+
|
|
5861
|
+
if (this.options.debug) {
|
|
5862
|
+
console.log(`🏷️ Watermark position updated: bottom ${this.watermarkElement.style.bottom}`);
|
|
5863
|
+
}
|
|
5864
|
+
}
|
|
5865
|
+
}
|
|
5866
|
+
|
|
5867
|
+
|
|
5704
5868
|
setWatermarkAutoHide(hide) {
|
|
5705
5869
|
this.options.hideWatermark = hide;
|
|
5706
5870
|
|
|
@@ -6386,15 +6550,29 @@ getBufferedTime() {
|
|
|
6386
6550
|
this.video.currentTime = Math.max(0, Math.min(this.video.duration, this.video.currentTime + seconds));
|
|
6387
6551
|
}
|
|
6388
6552
|
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
|
|
6553
|
+
updateTimeDisplay() {
|
|
6554
|
+
|
|
6555
|
+
if (this.currentTimeEl && this.video) {
|
|
6556
|
+
this.currentTimeEl.textContent = this.formatTime(this.video.currentTime || 0);
|
|
6557
|
+
}
|
|
6558
|
+
|
|
6559
|
+
|
|
6560
|
+
if (this.durationEl && this.video) {
|
|
6561
|
+
const duration = this.video.duration;
|
|
6393
6562
|
|
|
6394
|
-
|
|
6395
|
-
|
|
6563
|
+
|
|
6564
|
+
if (!duration || isNaN(duration) || !isFinite(duration)) {
|
|
6565
|
+
|
|
6566
|
+
this.durationEl.innerHTML = '<span class="encoding-badge">Encoding in progress</span>';
|
|
6567
|
+
this.durationEl.classList.add('encoding-state');
|
|
6568
|
+
} else {
|
|
6569
|
+
|
|
6570
|
+
this.durationEl.textContent = this.formatTime(duration);
|
|
6571
|
+
this.durationEl.classList.remove('encoding-state');
|
|
6396
6572
|
}
|
|
6397
6573
|
}
|
|
6574
|
+
}
|
|
6575
|
+
|
|
6398
6576
|
|
|
6399
6577
|
formatTime(seconds) {
|
|
6400
6578
|
if (isNaN(seconds) || seconds < 0) return '0:00';
|
package/package.json
CHANGED
|
@@ -23,10 +23,12 @@ Official Cloudflare Stream integration plugin for MYETV Video Player. Embed vide
|
|
|
23
23
|
## Features
|
|
24
24
|
|
|
25
25
|
- **Cloudflare Stream Integration**: Full support for Cloudflare Stream videos
|
|
26
|
+
- **DASH/HLS** adaptive streaming ready with full support and libraries hosted on cdnjs.com
|
|
26
27
|
- **Private Videos**: Support for signed URLs and private video access
|
|
27
28
|
- **Live Streaming**: Real-time live stream playback
|
|
28
29
|
- **Player Customization**: Custom colors, poster images, and branding
|
|
29
30
|
- **Auto-Detection**: Automatically detects Cloudflare Stream URLs
|
|
31
|
+
- **Auto-Quality**: Automatically detect video quality (also for hls/dash)
|
|
30
32
|
- **Complete API**: Full control over playback, volume, seeking
|
|
31
33
|
- **Analytics Ready**: Works with Cloudflare Analytics
|
|
32
34
|
- **Ad Support**: VAST ad tag integration
|
|
@@ -122,6 +124,7 @@ const player = new MYETVPlayer('myVideo', {
|
|
|
122
124
|
preload: 'metadata', // 'none', 'metadata', 'auto'
|
|
123
125
|
controls: true, // Show player controls
|
|
124
126
|
startTime: 0, // Start position in seconds
|
|
127
|
+
defaultQuality: 'auto', // the default quality of the video (auto, 720p, 480p, 360p ecc.)
|
|
125
128
|
|
|
126
129
|
// ========== Player Customization ==========
|
|
127
130
|
poster: 'https://example.com/poster.jpg', // Custom poster image
|
|
@@ -272,15 +275,34 @@ plugins: {
|
|
|
272
275
|
});
|
|
273
276
|
</script>
|
|
274
277
|
```
|
|
275
|
-
|
|
276
278
|
**Supported URL Formats:**
|
|
277
|
-
- `https://iframe.videodelivery.net/VIDEO_ID`
|
|
278
|
-
- `https://customer-
|
|
279
|
+
- `https://iframe.videodelivery.net/VIDEO_ID` (iframe)
|
|
280
|
+
- `https://customer-[code].cloudflarestream.com/VIDEO_ID/iframe` (iframe)
|
|
279
281
|
- `https://videodelivery.net/VIDEO_ID`
|
|
280
282
|
|
|
283
|
+
### Method 4: HLS / DASH Adaptive Streaming
|
|
284
|
+
|
|
285
|
+
```html
|
|
286
|
+
<video id="myVideo" class="video-player"></video>
|
|
287
|
+
|
|
288
|
+
<script>
|
|
289
|
+
const player = new MYETVPlayer('myVideo', {
|
|
290
|
+
plugins: {
|
|
291
|
+
cloudflare: {
|
|
292
|
+
manifestUrl: 'https://customer-[code].cloudflarestream.com/VIDEO_ID/manifest/video.m3u8'
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
</script>
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
**Supported URL Formats:**
|
|
300
|
+
- `https://customer-[code].cloudflarestream.com/VIDEO_ID/manifest/video.m3u8` (hls)
|
|
301
|
+
- `https://customer-[code].cloudflarestream.com/VIDEO_ID/manifest/video.mpd` (dash)
|
|
302
|
+
|
|
281
303
|
---
|
|
282
304
|
|
|
283
|
-
### Method
|
|
305
|
+
### Method 5: Load Dynamically
|
|
284
306
|
|
|
285
307
|
```html
|
|
286
308
|
<video id="myVideo" class="video-player"></video>
|