myetv-player 1.5.0 → 1.6.1
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 +66 -1
- package/css/myetv-player.css +15 -1
- package/css/myetv-player.min.css +1 -1
- package/dist/myetv-player.js +590 -56
- package/dist/myetv-player.min.js +524 -30
- package/package.json +4 -2
- package/plugins/google-adsense-ads/README.md +230 -0
- package/plugins/google-adsense-ads/g-adsense-ads-plugin.js +75 -8
- package/plugins/google-ima-ads/README.md +258 -0
- package/plugins/google-ima-ads/g-ima-ads-plugin.js +2 -2
- package/plugins/iframe-ads/myetv-iframe-banner-ads.js +926 -0
- package/plugins/iframe-ads/readme.md +191 -0
- package/plugins/vast-vpaid-ads/README.md +146 -0
- package/plugins/youtube/myetv-player-youtube-plugin.js +72 -39
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# MyeTV Iframe Banner Ads Plugin
|
|
2
|
+
|
|
3
|
+
Display customizable iframe banner advertisements with countdown timer and auto-close functionality for MyeTV Video Player.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Dual Mode Support**: Direct iframe URL or external ad script injection
|
|
8
|
+
- **Auto-close Timer**: Countdown with customizable duration
|
|
9
|
+
- **Repeat Intervals**: Show ads periodically during playback
|
|
10
|
+
- **Frequency Control**: Set minimum time between ad displays (with cookie tracking)
|
|
11
|
+
- **Responsive Design**: Adapts to different screen sizes (desktop/tablet/mobile)
|
|
12
|
+
- **Customizable**: Opacity, duration, position, and behavior options
|
|
13
|
+
- **User Control**: Optional close button for manual dismissal
|
|
14
|
+
- **Smart Positioning**: Auto-adjusts to compact button mode on small screens
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
Include the plugin after the MyeTV Player script:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
<script src="dist/myetv-player.js"></script>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
<script src="plugins/myetv-iframe-banner-ads.js"></script>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
### Method 1: Direct Iframe URL
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
const player = new MYETVvideoplayer('videoElement', {
|
|
34
|
+
plugins: {
|
|
35
|
+
'iframe-banner-ads': {
|
|
36
|
+
url: 'https://your-ads-server.com/banner.html',
|
|
37
|
+
duration: 10,
|
|
38
|
+
opacity: 0.8
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Method 2: External Ad Script
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
const player = new MYETVvideoplayer('videoElement', {
|
|
48
|
+
plugins: {
|
|
49
|
+
'iframe-banner-ads': {
|
|
50
|
+
adScript: 'https://store.myetv.tv/ads/CDN/publishers/adcode.js',
|
|
51
|
+
adParams: {
|
|
52
|
+
'data-size': '728x90',
|
|
53
|
+
'data-language': 'en',
|
|
54
|
+
'data-country': 'all',
|
|
55
|
+
'data-category': 'all',
|
|
56
|
+
'data-uidcode': 'your-unique-code-here'
|
|
57
|
+
},
|
|
58
|
+
duration: 10,
|
|
59
|
+
opacity: 0.85
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Configuration Options
|
|
66
|
+
|
|
67
|
+
| Option | Type | Default | Description |
|
|
68
|
+
|--------|------|---------|-------------|
|
|
69
|
+
| `url` | String | `''` | Direct iframe URL (Method 1) |
|
|
70
|
+
| `adScript` | String | `''` | External ad script URL (Method 2) |
|
|
71
|
+
| `adParams` | Object | `{}` | Data attributes for ad script (e.g., `data-size`, `data-uidcode`) |
|
|
72
|
+
| `duration` | Number | `5` | Auto-close timer duration in seconds |
|
|
73
|
+
| `opacity` | Number | `0.85` | Background opacity (0.0 - 1.0) |
|
|
74
|
+
| `showOnPlay` | Boolean | `true` | Show banner when video starts playing |
|
|
75
|
+
| `showOnPause` | Boolean | `false` | Show banner when video is paused |
|
|
76
|
+
| `closeable` | Boolean | `true` | Display close button (X) |
|
|
77
|
+
| `minTimeBetweenAds` | Number | `0` | Minimum seconds between ad displays (uses cookie) |
|
|
78
|
+
| `repeatInterval` | Number | `0` | Show ad every X seconds during playback |
|
|
79
|
+
| `cookieName` | String | `'myetv_last_ad_timestamp'` | Cookie name for tracking ad frequency |
|
|
80
|
+
| `debug` | Boolean | `false` | Enable console logging |
|
|
81
|
+
|
|
82
|
+
## Advanced Examples
|
|
83
|
+
|
|
84
|
+
### Frequency Control with Repeat Interval
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
const player = new MYETVvideoplayer('videoElement', {
|
|
88
|
+
plugins: {
|
|
89
|
+
'iframe-banner-ads': {
|
|
90
|
+
url: 'https://your-ads.com/banner.html',
|
|
91
|
+
duration: 10,
|
|
92
|
+
minTimeBetweenAds: 60, // Don't show ads more than once per minute
|
|
93
|
+
repeatInterval: 90, // Show ad every 90 seconds during video
|
|
94
|
+
debug: false
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Behavior:**
|
|
101
|
+
- Video starts → Ad shown
|
|
102
|
+
- Ad closes after 7 seconds
|
|
103
|
+
- After 90 seconds → Ad shown again (if `minTimeBetweenAds` allows)
|
|
104
|
+
- Continues until video ends
|
|
105
|
+
|
|
106
|
+
### Custom Ad Script with All Options
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
const player = new MYETVvideoplayer('videoElement', {
|
|
110
|
+
plugins: {
|
|
111
|
+
'iframe-banner-ads': {
|
|
112
|
+
adScript: 'https://store.myetv.tv/ads/CDN/publishers/adcode.js',
|
|
113
|
+
adParams: {
|
|
114
|
+
'data-size': '728x90',
|
|
115
|
+
'data-uidcode': 'yourusercode'
|
|
116
|
+
},
|
|
117
|
+
duration: 10,
|
|
118
|
+
opacity: 0.9,
|
|
119
|
+
showOnPlay: true,
|
|
120
|
+
showOnPause: false,
|
|
121
|
+
closeable: true,
|
|
122
|
+
minTimeBetweenAds: 120,
|
|
123
|
+
repeatInterval: 0, // Disabled
|
|
124
|
+
debug: false
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## API Methods
|
|
131
|
+
|
|
132
|
+
Access plugin instance:
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
const adsPlugin = player.getPlugin('iframe-banner-ads');
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Available Methods
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
// Show/hide banner manually
|
|
142
|
+
adsPlugin.show();
|
|
143
|
+
adsPlugin.hide();
|
|
144
|
+
adsPlugin.toggle();
|
|
145
|
+
|
|
146
|
+
// Update configuration
|
|
147
|
+
adsPlugin.setUrl('https://new-url.com/banner.html'); // Iframe mode only
|
|
148
|
+
adsPlugin.setAdParams({ 'data-size': '320x50' }); // Script mode only
|
|
149
|
+
adsPlugin.setDuration(10);
|
|
150
|
+
adsPlugin.setOpacity(0.75);
|
|
151
|
+
|
|
152
|
+
// Get current state
|
|
153
|
+
const state = adsPlugin.getState();
|
|
154
|
+
console.log(state.isVisible, state.adShownCount);
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Responsive Behavior
|
|
158
|
+
|
|
159
|
+
The plugin automatically adapts to screen size:
|
|
160
|
+
|
|
161
|
+
- **Desktop/Tablet** (> 640px): Full-width banner centered above controls
|
|
162
|
+
- **Small Screens** (≤ 640px × ≤ 500px): Compact button mode (25% width, left-aligned)
|
|
163
|
+
- **Mobile Portrait** (< 480px, tall): Full-width with reduced height
|
|
164
|
+
|
|
165
|
+
## Cookie Management
|
|
166
|
+
|
|
167
|
+
When `minTimeBetweenAds` is set, the plugin uses a cookie to track the last ad timestamp:
|
|
168
|
+
|
|
169
|
+
- **Cookie Name**: `myetv_last_ad_timestamp` (customizable via `cookieName` option)
|
|
170
|
+
- **Duration**: 2× `minTimeBetweenAds` value
|
|
171
|
+
- **SameSite**: `Lax`
|
|
172
|
+
- **Path**: `/` (site-wide)
|
|
173
|
+
|
|
174
|
+
## Browser Support
|
|
175
|
+
|
|
176
|
+
- Chrome/Edge (latest)
|
|
177
|
+
- Firefox (latest)
|
|
178
|
+
- Safari (latest)
|
|
179
|
+
- Mobile browsers (iOS Safari, Chrome Mobile)
|
|
180
|
+
|
|
181
|
+
## License
|
|
182
|
+
|
|
183
|
+
MIT License - Created by [MyeTV](https://www.myetv.tv) & [Oskar Cosimo](https://oskarcosimo.com)
|
|
184
|
+
|
|
185
|
+
## Contributing
|
|
186
|
+
|
|
187
|
+
Issues and pull requests are welcome! Please visit [GitHub Repository](#) for more information.
|
|
188
|
+
|
|
189
|
+
## Support
|
|
190
|
+
|
|
191
|
+
For questions or support, please [open an issue](https://github.com/myetv-video-player-opensource/issues) or contact us at [https://support.myetv.tv](https://support.myetv.tv)
|
|
@@ -1 +1,147 @@
|
|
|
1
|
+
# VAST / VPAID Ads Plugin for MYETV Player
|
|
1
2
|
|
|
3
|
+
Lightweight JavaScript plugin to handle **VAST** (and basic VPAID) ads directly in the MYETV video player, without external dependencies or third‑party SDKs.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Load ads from a **VAST URL** (`vastUrl`) or injected VAST XML (`vastXML`).
|
|
10
|
+
- Pre‑roll ads that block content until the ad finishes or is skipped.
|
|
11
|
+
- Full‑screen overlay with “Ad” label and configurable “Skip ad” button.
|
|
12
|
+
- Automatic parsing of:
|
|
13
|
+
- `MediaFile` (MP4 preferred)
|
|
14
|
+
- `Impression`
|
|
15
|
+
- `ClickThrough` / `ClickTracking`
|
|
16
|
+
- `skipoffset` and `Duration`
|
|
17
|
+
- Impression and click tracking via image pixels.
|
|
18
|
+
- No dependency on Google IMA or other SDKs.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
Include the plugin file **after** the MYETV player:
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
// Generic example, adapt to your paths/build
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
<script src="dist/myetv-player.js"></script>
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
<script src="plugins/vast-vpaid-ads-plugin.js"></script>
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The plugin registers itself globally as `vast` via `window.registerMYETVPlugin('vast', VASTPlugin)`.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Basic usage
|
|
45
|
+
|
|
46
|
+
### Initialize via player configuration
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
const player = new MYETVPlayer({
|
|
50
|
+
// ... other player options
|
|
51
|
+
plugins: {
|
|
52
|
+
vast: {
|
|
53
|
+
vastUrl: 'https://example.com/path/to/vast.xml',
|
|
54
|
+
debug: true
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
### Activate after initialization
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
const player = new MYETVPlayer({ /* ... */ });
|
|
65
|
+
|
|
66
|
+
player.usePlugin('vast', {
|
|
67
|
+
vastUrl: 'https://example.com/path/to/vast.xml',
|
|
68
|
+
debug: true
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The plugin requests the ad on the **first video play** event, shows the pre‑roll, then resumes the main content.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Available options
|
|
77
|
+
|
|
78
|
+
All options can be passed at initialization (through `plugins.vast`) or via `usePlugin('vast', options)`.
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
| Option | Type | Default | Description |
|
|
82
|
+
| :-- | :-- | :-- | :-- |
|
|
83
|
+
| `vastUrl` | string | `''` | VAST URL to fetch the XML response from. If not set and `vastXML` is missing, the plugin throws an error. |
|
|
84
|
+
| `vastXML` | string | `null` | Preloaded VAST XML string, alternative to `vastUrl`. |
|
|
85
|
+
| `skipDelay` | number | `5` (sec) | Fallback delay (in seconds) before showing the “Skip ad” button, used when `skipoffset` is missing or invalid. |
|
|
86
|
+
| `maxRedirects` | number | `5` | Maximum number of supported VAST redirects (wrappers are only partially handled in this version). |
|
|
87
|
+
| `timeout` | number | `8000` (ms) | Timeout for the VAST `fetch` request. |
|
|
88
|
+
| `debug` | boolean | `false` | Enables more verbose logging in the console. |
|
|
89
|
+
| `adLabel` | string | `ADS` | Text for the label shown in the top‑left corner of the ad overlay. |
|
|
90
|
+
| `skipText` | string | `Skip AD` | Text for the skip button. |
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Flow
|
|
96
|
+
|
|
97
|
+
1. **Hook on `play`**
|
|
98
|
+
On the first play:
|
|
99
|
+
- If `vastUrl` or `vastXML` are configured, `loadVAST()` is called and the main content is paused.
|
|
100
|
+
2. **VAST loading and parsing**
|
|
101
|
+
- If `vastXML` is present, that string is used directly.
|
|
102
|
+
- Otherwise, the plugin performs `fetch(vastUrl)` and reads the response as text.
|
|
103
|
+
- The VAST is parsed using `DOMParser` and it extracts:
|
|
104
|
+
- one `MediaFile` (preferring MIME `video/mp4`)
|
|
105
|
+
- impressions, clickthrough, clicktracking
|
|
106
|
+
- skip offset and duration.
|
|
107
|
+
3. **Ad playback**
|
|
108
|
+
- A full‑screen overlay is created with:
|
|
109
|
+
- “Ad” label
|
|
110
|
+
- dedicated ad `<video>`
|
|
111
|
+
- “Skip ad” button (initially hidden).
|
|
112
|
+
- The ad video is played, impression URLs are fired, and the skip button is shown after `skipOffset` seconds (or the `skipDelay` fallback).
|
|
113
|
+
4. **End or skip**
|
|
114
|
+
- When the ad ends or is skipped:
|
|
115
|
+
- overlay and button are hidden
|
|
116
|
+
- the ad video source is cleared
|
|
117
|
+
- the main video resumes (`this.player.video.play()`).
|
|
118
|
+
- The following events are triggered on the player: `adstarted`, `adcomplete`, `adskipped`, `aderror`.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Events
|
|
123
|
+
|
|
124
|
+
The plugin emits several events on the player, useful for UI and analytics:
|
|
125
|
+
|
|
126
|
+
- `adstarted` – when the ad starts
|
|
127
|
+
- `adcomplete` – when the ad finishes normally
|
|
128
|
+
- `adskipped` – when the user skips the ad
|
|
129
|
+
- `aderror` – when there is a VAST loading/parsing error
|
|
130
|
+
|
|
131
|
+
Example:
|
|
132
|
+
|
|
133
|
+
```javascript
|
|
134
|
+
player.on('adstarted', () => console.log('Ad started'));
|
|
135
|
+
player.on('adcomplete', () => console.log('Ad completed'));
|
|
136
|
+
player.on('adskipped', () => console.log('Ad skipped'));
|
|
137
|
+
player.on('aderror', e => console.error('Ad error', e));
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Notes and limitations
|
|
144
|
+
|
|
145
|
+
- Full support for VAST **InLine**; **Wrapper** responses are detected but not deeply followed in this version.
|
|
146
|
+
- The plugin prefers a `MediaFile` in MP4; other formats depend on browser support.
|
|
147
|
+
- Make sure your ad server returns a valid VAST (2.0/3.0) with at least one compatible `MediaFile`.
|
|
@@ -3434,11 +3434,67 @@
|
|
|
3434
3434
|
'3': 'BUFFERING',
|
|
3435
3435
|
'5': 'CUED'
|
|
3436
3436
|
};
|
|
3437
|
-
if (this.api.player.options.debug) console.log('[YT Plugin] State:', states[event.data], event.data);
|
|
3438
3437
|
|
|
3439
|
-
|
|
3438
|
+
if (this.api.player.options.debug)
|
|
3439
|
+
console.log('YT Plugin State:', states[event.data], event.data);
|
|
3440
|
+
|
|
3441
|
+
// Handle auto-hide based on YouTube state
|
|
3442
|
+
if (event.data === YT.PlayerState.PAUSED) {
|
|
3443
|
+
// Video paused: show controls and CANCEL timer
|
|
3444
|
+
if (this.api.player.showControlsNow) {
|
|
3445
|
+
this.api.player.showControlsNow();
|
|
3446
|
+
}
|
|
3447
|
+
// Clear timer to prevent controls from disappearing
|
|
3448
|
+
if (this.api.player.autoHideTimer) {
|
|
3449
|
+
clearTimeout(this.api.player.autoHideTimer);
|
|
3450
|
+
this.api.player.autoHideTimer = null;
|
|
3451
|
+
}
|
|
3452
|
+
// Add CSS class
|
|
3453
|
+
if (this.api.container) {
|
|
3454
|
+
this.api.container.classList.add('video-paused');
|
|
3455
|
+
}
|
|
3456
|
+
|
|
3457
|
+
if (this.api.player.options.debug)
|
|
3458
|
+
console.log('YT Plugin: Video paused - controls locked visible');
|
|
3459
|
+
}
|
|
3460
|
+
|
|
3461
|
+
if (event.data === YT.PlayerState.PLAYING) {
|
|
3462
|
+
// Video playing: remove class and restart auto-hide
|
|
3463
|
+
if (this.api.container) {
|
|
3464
|
+
this.api.container.classList.remove('video-paused');
|
|
3465
|
+
}
|
|
3466
|
+
// Restart auto-hide only if enabled
|
|
3467
|
+
if (this.api.player.options.autoHide && this.api.player.autoHideInitialized) {
|
|
3468
|
+
if (this.api.player.showControlsNow) {
|
|
3469
|
+
this.api.player.showControlsNow();
|
|
3470
|
+
}
|
|
3471
|
+
if (this.api.player.resetAutoHideTimer) {
|
|
3472
|
+
this.api.player.resetAutoHideTimer();
|
|
3473
|
+
}
|
|
3474
|
+
}
|
|
3475
|
+
|
|
3476
|
+
if (this.api.player.options.debug)
|
|
3477
|
+
console.log('YT Plugin: Video playing - auto-hide restarted');
|
|
3478
|
+
}
|
|
3479
|
+
|
|
3480
|
+
// Handle when video fails to autoplay (stays in UNSTARTED)
|
|
3481
|
+
if (event.data === YT.PlayerState.UNSTARTED || event.data === -1) {
|
|
3482
|
+
// Show controls and block auto-hide
|
|
3483
|
+
if (this.api.player.showControlsNow) {
|
|
3484
|
+
this.api.player.showControlsNow();
|
|
3485
|
+
}
|
|
3486
|
+
// Clear any active timer
|
|
3487
|
+
if (this.api.player.autoHideTimer) {
|
|
3488
|
+
clearTimeout(this.api.player.autoHideTimer);
|
|
3489
|
+
this.api.player.autoHideTimer = null;
|
|
3490
|
+
}
|
|
3491
|
+
|
|
3492
|
+
if (this.api.player.options.debug)
|
|
3493
|
+
console.log('YT Plugin: Video UNSTARTED (autoplay blocked?) - controls locked visible');
|
|
3494
|
+
}
|
|
3495
|
+
|
|
3496
|
+
// Start timeout when video is unstarted
|
|
3440
3497
|
if (event.data === YT.PlayerState.UNSTARTED || event.data === -1) {
|
|
3441
|
-
// start timeout when video is unstarted
|
|
3442
3498
|
if (this.playAttemptTimeout) {
|
|
3443
3499
|
clearTimeout(this.playAttemptTimeout);
|
|
3444
3500
|
}
|
|
@@ -3448,15 +3504,14 @@
|
|
|
3448
3504
|
|
|
3449
3505
|
const currentState = this.ytPlayer.getPlayerState();
|
|
3450
3506
|
|
|
3451
|
-
// If video is
|
|
3507
|
+
// If video is unstarted after timeout, consider it restricted
|
|
3452
3508
|
if (currentState === YT.PlayerState.UNSTARTED || currentState === -1) {
|
|
3453
|
-
if (this.api.player.options.debug)
|
|
3509
|
+
if (this.api.player.options.debug)
|
|
3454
3510
|
console.log('YT Plugin: Video stuck in UNSTARTED - possibly members-only or restricted');
|
|
3455
|
-
}
|
|
3456
3511
|
|
|
3457
3512
|
// Trigger ended event
|
|
3458
3513
|
this.api.triggerEvent('ended', {
|
|
3459
|
-
reason: '
|
|
3514
|
+
reason: 'videorestrictedormembership',
|
|
3460
3515
|
state: currentState
|
|
3461
3516
|
});
|
|
3462
3517
|
|
|
@@ -3464,14 +3519,12 @@
|
|
|
3464
3519
|
this.showPosterOverlay();
|
|
3465
3520
|
|
|
3466
3521
|
// Trigger custom event
|
|
3467
|
-
this.api.triggerEvent('
|
|
3522
|
+
this.api.triggerEvent('youtubepluginmembershiprestricted', {
|
|
3468
3523
|
videoId: this.videoId
|
|
3469
3524
|
});
|
|
3470
3525
|
}
|
|
3471
|
-
}, 15000); // 15 seconds
|
|
3472
|
-
|
|
3473
|
-
} else if (event.data === YT.PlayerState.PLAYING ||
|
|
3474
|
-
event.data === YT.PlayerState.BUFFERING) {
|
|
3526
|
+
}, 15000); // 15 seconds timeout
|
|
3527
|
+
} else if (event.data === YT.PlayerState.PLAYING || event.data === YT.PlayerState.BUFFERING) {
|
|
3475
3528
|
// Clear the timeout if video starts correctly
|
|
3476
3529
|
if (this.playAttemptTimeout) {
|
|
3477
3530
|
clearTimeout(this.playAttemptTimeout);
|
|
@@ -3488,9 +3541,8 @@
|
|
|
3488
3541
|
|
|
3489
3542
|
// Handle live stream ended
|
|
3490
3543
|
if (this.isLiveStream && event.data === YT.PlayerState.ENDED) {
|
|
3491
|
-
if (this.api.player.options.debug)
|
|
3492
|
-
console.log('
|
|
3493
|
-
}
|
|
3544
|
+
if (this.api.player.options.debug)
|
|
3545
|
+
console.log('YT Plugin: Live stream ended (player state ENDED)');
|
|
3494
3546
|
this.handleLiveStreamEnded();
|
|
3495
3547
|
return;
|
|
3496
3548
|
}
|
|
@@ -3500,24 +3552,19 @@
|
|
|
3500
3552
|
if (event.data === YT.PlayerState.PAUSED) {
|
|
3501
3553
|
// Orange when paused during live
|
|
3502
3554
|
badge.style.background = '#ff8800';
|
|
3503
|
-
badge.textContent = '
|
|
3555
|
+
badge.textContent = 'LIVE';
|
|
3504
3556
|
badge.title = 'Livestreaming in Pause';
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
console.log('[YT Plugin] 🟠 Live paused');
|
|
3508
|
-
}
|
|
3557
|
+
if (this.api.player.options.debug)
|
|
3558
|
+
console.log('YT Plugin: Live paused');
|
|
3509
3559
|
} else if (event.data === YT.PlayerState.PLAYING) {
|
|
3510
3560
|
// Red when playing (will be checked for de-sync below)
|
|
3511
3561
|
badge.style.background = '#ff0000';
|
|
3512
3562
|
badge.textContent = 'LIVE';
|
|
3513
3563
|
badge.title = 'Livestreaming';
|
|
3514
|
-
|
|
3515
|
-
if (this.api.player.options.debug) {
|
|
3516
|
-
console.log('[YT Plugin] 🔴 Live playing');
|
|
3517
|
-
}
|
|
3518
3564
|
}
|
|
3519
3565
|
}
|
|
3520
3566
|
|
|
3567
|
+
// Handle state changes
|
|
3521
3568
|
switch (event.data) {
|
|
3522
3569
|
case YT.PlayerState.PLAYING:
|
|
3523
3570
|
this.api.triggerEvent('played', {});
|
|
@@ -3549,27 +3596,13 @@
|
|
|
3549
3596
|
case YT.PlayerState.ENDED:
|
|
3550
3597
|
this.api.triggerEvent('ended', {});
|
|
3551
3598
|
|
|
3552
|
-
// Show play icon
|
|
3599
|
+
// Show play icon for replay
|
|
3553
3600
|
if (playIcon && pauseIcon) {
|
|
3554
3601
|
playIcon.classList.remove('hidden');
|
|
3555
3602
|
pauseIcon.classList.add('hidden');
|
|
3556
3603
|
}
|
|
3557
3604
|
break;
|
|
3558
3605
|
}
|
|
3559
|
-
|
|
3560
|
-
if (event.data === YT.PlayerState.PAUSED) {
|
|
3561
|
-
// add pause class
|
|
3562
|
-
if (this.api.container) {
|
|
3563
|
-
this.api.container.classList.add('video-paused');
|
|
3564
|
-
}
|
|
3565
|
-
}
|
|
3566
|
-
|
|
3567
|
-
if (event.data === YT.PlayerState.PLAYING) {
|
|
3568
|
-
// remove pause class
|
|
3569
|
-
if (this.api.container) {
|
|
3570
|
-
this.api.container.classList.remove('video-paused');
|
|
3571
|
-
}
|
|
3572
|
-
}
|
|
3573
3606
|
}
|
|
3574
3607
|
|
|
3575
3608
|
onPlaybackQualityChange(event) {
|