unified-video-framework 1.4.151 → 1.4.154
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/package.json +1 -1
- package/packages/core/dist/chapter-manager.d.ts +39 -0
- package/packages/core/dist/chapter-manager.d.ts.map +1 -0
- package/packages/core/dist/chapter-manager.js +173 -0
- package/packages/core/dist/chapter-manager.js.map +1 -0
- package/packages/core/dist/index.d.ts +2 -0
- package/packages/core/dist/index.d.ts.map +1 -1
- package/packages/core/dist/index.js +1 -0
- package/packages/core/dist/index.js.map +1 -1
- package/packages/core/dist/interfaces/IVideoPlayer.d.ts +10 -0
- package/packages/core/dist/interfaces/IVideoPlayer.d.ts.map +1 -1
- package/packages/core/dist/interfaces.d.ts +33 -1
- package/packages/core/dist/interfaces.d.ts.map +1 -1
- package/packages/core/src/chapter-manager.ts +290 -0
- package/packages/core/src/index.ts +4 -0
- package/packages/core/src/interfaces/IVideoPlayer.ts +11 -0
- package/packages/core/src/interfaces.ts +47 -1
- package/packages/web/dist/WebPlayer.d.ts +24 -1
- package/packages/web/dist/WebPlayer.d.ts.map +1 -1
- package/packages/web/dist/WebPlayer.js +472 -1
- package/packages/web/dist/WebPlayer.js.map +1 -1
- package/packages/web/dist/chapters/ChapterManager.d.ts +38 -0
- package/packages/web/dist/chapters/ChapterManager.d.ts.map +1 -0
- package/packages/web/dist/chapters/ChapterManager.js +291 -0
- package/packages/web/dist/chapters/ChapterManager.js.map +1 -0
- package/packages/web/dist/chapters/SkipButtonController.d.ts +31 -0
- package/packages/web/dist/chapters/SkipButtonController.d.ts.map +1 -0
- package/packages/web/dist/chapters/SkipButtonController.js +213 -0
- package/packages/web/dist/chapters/SkipButtonController.js.map +1 -0
- package/packages/web/dist/chapters/UserPreferencesManager.d.ts +25 -0
- package/packages/web/dist/chapters/UserPreferencesManager.d.ts.map +1 -0
- package/packages/web/dist/chapters/UserPreferencesManager.js +232 -0
- package/packages/web/dist/chapters/UserPreferencesManager.js.map +1 -0
- package/packages/web/dist/chapters/index.d.ts +12 -0
- package/packages/web/dist/chapters/index.d.ts.map +1 -0
- package/packages/web/dist/chapters/index.js +8 -0
- package/packages/web/dist/chapters/index.js.map +1 -0
- package/packages/web/dist/chapters/types/ChapterTypes.d.ts +98 -0
- package/packages/web/dist/chapters/types/ChapterTypes.d.ts.map +1 -0
- package/packages/web/dist/chapters/types/ChapterTypes.js +31 -0
- package/packages/web/dist/chapters/types/ChapterTypes.js.map +1 -0
- package/packages/web/dist/index.d.ts +1 -1
- package/packages/web/dist/index.d.ts.map +1 -1
- package/packages/web/dist/index.js +1 -1
- package/packages/web/dist/index.js.map +1 -1
- package/packages/web/dist/react/WebPlayerView.d.ts +2 -2
- package/packages/web/dist/react/WebPlayerView.d.ts.map +1 -1
- package/packages/web/dist/react/WebPlayerViewWithEPG.d.ts +2 -2
- package/packages/web/dist/react/WebPlayerViewWithEPG.d.ts.map +1 -1
- package/packages/web/dist/react/components/ChapterProgress.d.ts +22 -0
- package/packages/web/dist/react/components/ChapterProgress.d.ts.map +1 -0
- package/packages/web/dist/react/components/ChapterProgress.js +101 -0
- package/packages/web/dist/react/components/ChapterProgress.js.map +1 -0
- package/packages/web/dist/react/components/SkipButton.d.ts +18 -0
- package/packages/web/dist/react/components/SkipButton.d.ts.map +1 -0
- package/packages/web/dist/react/components/SkipButton.js +156 -0
- package/packages/web/dist/react/components/SkipButton.js.map +1 -0
- package/packages/web/dist/react/hooks/useChapters.d.ts +29 -0
- package/packages/web/dist/react/hooks/useChapters.d.ts.map +1 -0
- package/packages/web/dist/react/hooks/useChapters.js +158 -0
- package/packages/web/dist/react/hooks/useChapters.js.map +1 -0
- package/packages/web/package.json +0 -3
- package/packages/web/src/SecureVideoPlayer.ts +1 -1
- package/packages/web/src/WebPlayer.ts +587 -3
- package/packages/web/src/__tests__/WebPlayer.test.ts +1 -1
- package/packages/web/src/__tests__/epg-integration.test.ts +1 -1
- package/packages/web/src/chapters/ChapterManager.ts +464 -0
- package/packages/web/src/chapters/SkipButtonController.ts +353 -0
- package/packages/web/src/chapters/UserPreferencesManager.ts +324 -0
- package/packages/web/src/chapters/index.ts +34 -0
- package/packages/web/src/chapters/types/ChapterTypes.ts +236 -0
- package/packages/web/src/index.ts +1 -1
- package/packages/web/src/react/EPG.ts +1 -1
- package/packages/web/src/react/WebPlayerView.tsx +2 -2
- package/packages/web/src/react/WebPlayerViewWithEPG.tsx +3 -3
- package/packages/web/src/react/components/ChapterProgress.tsx +207 -0
- package/packages/web/src/react/components/EPGNavigationControls.tsx +1 -1
- package/packages/web/src/react/components/EPGOverlay-improved-positioning.tsx +1 -1
- package/packages/web/src/react/components/EPGOverlay.tsx +1 -1
- package/packages/web/src/react/components/EPGProgramGrid.tsx +1 -1
- package/packages/web/src/react/components/EPGTimelineHeader.tsx +1 -1
- package/packages/web/src/react/components/SkipButton.tsx +278 -0
- package/packages/web/src/react/hooks/useChapters.ts +308 -0
- package/packages/web/src/react/types/EPGTypes.ts +1 -1
- package/packages/web/src/react/utils/EPGUtils.ts +1 -1
- package/packages/web/src/test/epg-test.ts +1 -1
|
@@ -2,14 +2,24 @@
|
|
|
2
2
|
* Web implementation of the video player with HLS and DASH support
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { BasePlayer } from '
|
|
5
|
+
import { BasePlayer } from '../../core/src/BasePlayer';
|
|
6
6
|
import {
|
|
7
7
|
VideoSource,
|
|
8
8
|
PlayerConfig,
|
|
9
9
|
Quality,
|
|
10
10
|
SubtitleTrack,
|
|
11
|
-
PlayerError
|
|
12
|
-
|
|
11
|
+
PlayerError,
|
|
12
|
+
ChapterManager as CoreChapterManager,
|
|
13
|
+
Chapter,
|
|
14
|
+
ChapterSegment
|
|
15
|
+
} from '../../core/src/index';
|
|
16
|
+
import { ChapterManager } from './chapters/ChapterManager';
|
|
17
|
+
import {
|
|
18
|
+
ChapterConfig,
|
|
19
|
+
VideoChapters,
|
|
20
|
+
VideoSegment,
|
|
21
|
+
ChapterEvents
|
|
22
|
+
} from './chapters/types/ChapterTypes';
|
|
13
23
|
|
|
14
24
|
// Dynamic imports for streaming libraries
|
|
15
25
|
declare global {
|
|
@@ -86,6 +96,11 @@ export class WebPlayer extends BasePlayer {
|
|
|
86
96
|
|
|
87
97
|
// Progress bar tooltip state
|
|
88
98
|
private showTimeTooltip: boolean = false;
|
|
99
|
+
|
|
100
|
+
// Chapter management
|
|
101
|
+
private chapterManager: ChapterManager | null = null;
|
|
102
|
+
private coreChapterManager: CoreChapterManager | null = null;
|
|
103
|
+
private chapterConfig: ChapterConfig = { enabled: false };
|
|
89
104
|
|
|
90
105
|
// Debug logging helper
|
|
91
106
|
private debugLog(message: string, ...args: any[]): void {
|
|
@@ -131,6 +146,32 @@ export class WebPlayer extends BasePlayer {
|
|
|
131
146
|
console.log('No settings config found, using defaults:', this.settingsConfig);
|
|
132
147
|
}
|
|
133
148
|
|
|
149
|
+
// Configure chapters if provided
|
|
150
|
+
if (config && config.chapters) {
|
|
151
|
+
console.log('Chapter config found:', config.chapters);
|
|
152
|
+
this.chapterConfig = {
|
|
153
|
+
enabled: config.chapters.enabled || false,
|
|
154
|
+
data: config.chapters.data,
|
|
155
|
+
dataUrl: config.chapters.dataUrl,
|
|
156
|
+
autoHide: config.chapters.autoHide !== undefined ? config.chapters.autoHide : true,
|
|
157
|
+
autoHideDelay: config.chapters.autoHideDelay || 5000,
|
|
158
|
+
showChapterMarkers: config.chapters.showChapterMarkers !== undefined ? config.chapters.showChapterMarkers : true,
|
|
159
|
+
skipButtonPosition: config.chapters.skipButtonPosition || 'bottom-right',
|
|
160
|
+
customStyles: config.chapters.customStyles || {},
|
|
161
|
+
userPreferences: config.chapters.userPreferences || {
|
|
162
|
+
autoSkipIntro: false,
|
|
163
|
+
autoSkipRecap: false,
|
|
164
|
+
autoSkipCredits: false,
|
|
165
|
+
showSkipButtons: true,
|
|
166
|
+
skipButtonTimeout: 5000,
|
|
167
|
+
rememberChoices: true
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
console.log('Chapter config applied:', this.chapterConfig);
|
|
171
|
+
} else {
|
|
172
|
+
console.log('No chapter config found, chapters disabled');
|
|
173
|
+
}
|
|
174
|
+
|
|
134
175
|
// Call parent initialize
|
|
135
176
|
await super.initialize(container, config);
|
|
136
177
|
}
|
|
@@ -198,6 +239,11 @@ export class WebPlayer extends BasePlayer {
|
|
|
198
239
|
this.setupWatermark();
|
|
199
240
|
this.setupFullscreenListeners();
|
|
200
241
|
this.setupUserInteractionTracking();
|
|
242
|
+
|
|
243
|
+
// Initialize chapter manager if enabled
|
|
244
|
+
if (this.chapterConfig.enabled && this.video) {
|
|
245
|
+
this.setupChapterManager();
|
|
246
|
+
}
|
|
201
247
|
|
|
202
248
|
// Initialize paywall controller if provided
|
|
203
249
|
try {
|
|
@@ -326,6 +372,10 @@ export class WebPlayer extends BasePlayer {
|
|
|
326
372
|
this.updateTime(t);
|
|
327
373
|
// Enforce free preview gate on local playback
|
|
328
374
|
this.enforceFreePreviewGate(t);
|
|
375
|
+
// Process chapter time updates
|
|
376
|
+
if (this.coreChapterManager) {
|
|
377
|
+
this.coreChapterManager.processTimeUpdate(t);
|
|
378
|
+
}
|
|
329
379
|
});
|
|
330
380
|
|
|
331
381
|
this.video.addEventListener('progress', () => {
|
|
@@ -2439,6 +2489,201 @@ export class WebPlayer extends BasePlayer {
|
|
|
2439
2489
|
transform: translateX(-50%) translateY(0);
|
|
2440
2490
|
}
|
|
2441
2491
|
|
|
2492
|
+
/* Chapter Markers */
|
|
2493
|
+
.uvf-chapter-marker {
|
|
2494
|
+
position: absolute;
|
|
2495
|
+
top: 0;
|
|
2496
|
+
width: 2px;
|
|
2497
|
+
height: 100%;
|
|
2498
|
+
background: rgba(255, 255, 255, 0.6);
|
|
2499
|
+
z-index: 4;
|
|
2500
|
+
cursor: pointer;
|
|
2501
|
+
transition: all 0.2s ease;
|
|
2502
|
+
}
|
|
2503
|
+
|
|
2504
|
+
.uvf-chapter-marker:hover {
|
|
2505
|
+
width: 3px;
|
|
2506
|
+
box-shadow: 0 0 8px rgba(255, 255, 255, 0.8);
|
|
2507
|
+
}
|
|
2508
|
+
|
|
2509
|
+
.uvf-chapter-marker-intro {
|
|
2510
|
+
background: var(--uvf-accent-1, #ff5722);
|
|
2511
|
+
}
|
|
2512
|
+
|
|
2513
|
+
.uvf-chapter-marker-recap {
|
|
2514
|
+
background: #ffc107;
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
.uvf-chapter-marker-credits {
|
|
2518
|
+
background: #9c27b0;
|
|
2519
|
+
}
|
|
2520
|
+
|
|
2521
|
+
.uvf-chapter-marker-ad {
|
|
2522
|
+
background: #f44336;
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2525
|
+
/* Skip Button Styles */
|
|
2526
|
+
.uvf-skip-button {
|
|
2527
|
+
position: absolute;
|
|
2528
|
+
background: rgba(0, 0, 0, 0.8);
|
|
2529
|
+
color: white;
|
|
2530
|
+
border: 2px solid rgba(255, 255, 255, 0.3);
|
|
2531
|
+
border-radius: 8px;
|
|
2532
|
+
padding: 12px 24px;
|
|
2533
|
+
font-size: 16px;
|
|
2534
|
+
font-weight: 600;
|
|
2535
|
+
cursor: pointer;
|
|
2536
|
+
backdrop-filter: blur(10px);
|
|
2537
|
+
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
|
|
2538
|
+
z-index: 1000;
|
|
2539
|
+
user-select: none;
|
|
2540
|
+
|
|
2541
|
+
/* Default hidden state */
|
|
2542
|
+
opacity: 0;
|
|
2543
|
+
transform: translateX(100px) scale(0.9);
|
|
2544
|
+
pointer-events: none;
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2547
|
+
.uvf-skip-button.visible {
|
|
2548
|
+
opacity: 1;
|
|
2549
|
+
transform: translateX(0) scale(1);
|
|
2550
|
+
pointer-events: auto;
|
|
2551
|
+
}
|
|
2552
|
+
|
|
2553
|
+
.uvf-skip-button:hover {
|
|
2554
|
+
background: var(--uvf-accent-1, #ff5722);
|
|
2555
|
+
border-color: var(--uvf-accent-1, #ff5722);
|
|
2556
|
+
transform: translateX(0) scale(1.05);
|
|
2557
|
+
box-shadow: 0 4px 20px rgba(255, 87, 34, 0.4);
|
|
2558
|
+
}
|
|
2559
|
+
|
|
2560
|
+
.uvf-skip-button:active {
|
|
2561
|
+
transform: translateX(0) scale(0.95);
|
|
2562
|
+
transition: all 0.1s ease;
|
|
2563
|
+
}
|
|
2564
|
+
|
|
2565
|
+
/* Skip button positioning */
|
|
2566
|
+
.uvf-skip-button-bottom-right {
|
|
2567
|
+
bottom: 100px;
|
|
2568
|
+
right: 30px;
|
|
2569
|
+
}
|
|
2570
|
+
|
|
2571
|
+
.uvf-skip-button-bottom-left {
|
|
2572
|
+
bottom: 100px;
|
|
2573
|
+
left: 30px;
|
|
2574
|
+
transform: translateX(-100px) scale(0.9);
|
|
2575
|
+
}
|
|
2576
|
+
|
|
2577
|
+
.uvf-skip-button-bottom-left.visible {
|
|
2578
|
+
transform: translateX(0) scale(1);
|
|
2579
|
+
}
|
|
2580
|
+
|
|
2581
|
+
.uvf-skip-button-bottom-left:hover {
|
|
2582
|
+
transform: translateX(0) scale(1.05);
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2585
|
+
.uvf-skip-button-bottom-left:active {
|
|
2586
|
+
transform: translateX(0) scale(0.95);
|
|
2587
|
+
}
|
|
2588
|
+
|
|
2589
|
+
.uvf-skip-button-top-right {
|
|
2590
|
+
top: 30px;
|
|
2591
|
+
right: 30px;
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
.uvf-skip-button-top-left {
|
|
2595
|
+
top: 30px;
|
|
2596
|
+
left: 30px;
|
|
2597
|
+
transform: translateX(-100px) scale(0.9);
|
|
2598
|
+
}
|
|
2599
|
+
|
|
2600
|
+
.uvf-skip-button-top-left.visible {
|
|
2601
|
+
transform: translateX(0) scale(1);
|
|
2602
|
+
}
|
|
2603
|
+
|
|
2604
|
+
.uvf-skip-button-top-left:hover {
|
|
2605
|
+
transform: translateX(0) scale(1.05);
|
|
2606
|
+
}
|
|
2607
|
+
|
|
2608
|
+
.uvf-skip-button-top-left:active {
|
|
2609
|
+
transform: translateX(0) scale(0.95);
|
|
2610
|
+
}
|
|
2611
|
+
|
|
2612
|
+
/* Skip button segment type styling */
|
|
2613
|
+
.uvf-skip-intro {
|
|
2614
|
+
border-color: var(--uvf-accent-1, #ff5722);
|
|
2615
|
+
}
|
|
2616
|
+
|
|
2617
|
+
.uvf-skip-intro:hover {
|
|
2618
|
+
background: var(--uvf-accent-1, #ff5722);
|
|
2619
|
+
border-color: var(--uvf-accent-1, #ff5722);
|
|
2620
|
+
}
|
|
2621
|
+
|
|
2622
|
+
.uvf-skip-recap {
|
|
2623
|
+
border-color: #ffc107;
|
|
2624
|
+
}
|
|
2625
|
+
|
|
2626
|
+
.uvf-skip-recap:hover {
|
|
2627
|
+
background: #ffc107;
|
|
2628
|
+
border-color: #ffc107;
|
|
2629
|
+
color: #000;
|
|
2630
|
+
}
|
|
2631
|
+
|
|
2632
|
+
.uvf-skip-credits {
|
|
2633
|
+
border-color: #9c27b0;
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2636
|
+
.uvf-skip-credits:hover {
|
|
2637
|
+
background: #9c27b0;
|
|
2638
|
+
border-color: #9c27b0;
|
|
2639
|
+
}
|
|
2640
|
+
|
|
2641
|
+
.uvf-skip-ad {
|
|
2642
|
+
border-color: #f44336;
|
|
2643
|
+
}
|
|
2644
|
+
|
|
2645
|
+
.uvf-skip-ad:hover {
|
|
2646
|
+
background: #f44336;
|
|
2647
|
+
border-color: #f44336;
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2650
|
+
/* Auto-skip countdown styling */
|
|
2651
|
+
.uvf-skip-button.auto-skip {
|
|
2652
|
+
position: relative;
|
|
2653
|
+
overflow: hidden;
|
|
2654
|
+
border-color: var(--uvf-accent-1, #ff5722);
|
|
2655
|
+
animation: uvf-skip-pulse 2s infinite;
|
|
2656
|
+
}
|
|
2657
|
+
|
|
2658
|
+
.uvf-skip-button.auto-skip::before {
|
|
2659
|
+
content: '';
|
|
2660
|
+
position: absolute;
|
|
2661
|
+
bottom: 0;
|
|
2662
|
+
left: 0;
|
|
2663
|
+
height: 3px;
|
|
2664
|
+
background: var(--uvf-accent-1, #ff5722);
|
|
2665
|
+
width: 0%;
|
|
2666
|
+
transition: none;
|
|
2667
|
+
z-index: -1;
|
|
2668
|
+
}
|
|
2669
|
+
|
|
2670
|
+
.uvf-skip-button.auto-skip.countdown::before {
|
|
2671
|
+
width: 100%;
|
|
2672
|
+
transition: width linear;
|
|
2673
|
+
}
|
|
2674
|
+
|
|
2675
|
+
@keyframes uvf-skip-pulse {
|
|
2676
|
+
0% {
|
|
2677
|
+
box-shadow: 0 0 0 0 rgba(255, 87, 34, 0.4);
|
|
2678
|
+
}
|
|
2679
|
+
50% {
|
|
2680
|
+
box-shadow: 0 0 0 8px rgba(255, 87, 34, 0.1);
|
|
2681
|
+
}
|
|
2682
|
+
100% {
|
|
2683
|
+
box-shadow: 0 0 0 0 rgba(255, 87, 34, 0);
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
|
|
2442
2687
|
|
|
2443
2688
|
|
|
2444
2689
|
/* Mobile responsive design with enhanced touch targets */
|
|
@@ -2463,6 +2708,42 @@ export class WebPlayer extends BasePlayer {
|
|
|
2463
2708
|
top: 2.5px; /* Center on the 5px mobile hover progress bar */
|
|
2464
2709
|
}
|
|
2465
2710
|
|
|
2711
|
+
/* Mobile skip button adjustments */
|
|
2712
|
+
.uvf-skip-button {
|
|
2713
|
+
padding: 10px 20px;
|
|
2714
|
+
font-size: 14px;
|
|
2715
|
+
border-radius: 6px;
|
|
2716
|
+
}
|
|
2717
|
+
|
|
2718
|
+
.uvf-skip-button-bottom-right {
|
|
2719
|
+
bottom: 80px;
|
|
2720
|
+
right: 20px;
|
|
2721
|
+
}
|
|
2722
|
+
|
|
2723
|
+
.uvf-skip-button-bottom-left {
|
|
2724
|
+
bottom: 80px;
|
|
2725
|
+
left: 20px;
|
|
2726
|
+
}
|
|
2727
|
+
|
|
2728
|
+
.uvf-skip-button-top-right {
|
|
2729
|
+
top: 20px;
|
|
2730
|
+
right: 20px;
|
|
2731
|
+
}
|
|
2732
|
+
|
|
2733
|
+
.uvf-skip-button-top-left {
|
|
2734
|
+
top: 20px;
|
|
2735
|
+
left: 20px;
|
|
2736
|
+
}
|
|
2737
|
+
|
|
2738
|
+
/* Mobile chapter markers */
|
|
2739
|
+
.uvf-chapter-marker {
|
|
2740
|
+
width: 3px; /* Thicker on mobile for better touch */
|
|
2741
|
+
}
|
|
2742
|
+
|
|
2743
|
+
.uvf-chapter-marker:hover {
|
|
2744
|
+
width: 4px;
|
|
2745
|
+
}
|
|
2746
|
+
|
|
2466
2747
|
}
|
|
2467
2748
|
|
|
2468
2749
|
/* Controls Row */
|
|
@@ -6619,6 +6900,299 @@ export class WebPlayer extends BasePlayer {
|
|
|
6619
6900
|
}
|
|
6620
6901
|
}
|
|
6621
6902
|
|
|
6903
|
+
/**
|
|
6904
|
+
* Setup chapter manager for skip functionality
|
|
6905
|
+
*/
|
|
6906
|
+
private setupChapterManager(): void {
|
|
6907
|
+
if (!this.video || !this.playerWrapper) {
|
|
6908
|
+
this.debugWarn('Cannot setup chapter manager: video or wrapper not available');
|
|
6909
|
+
return;
|
|
6910
|
+
}
|
|
6911
|
+
|
|
6912
|
+
try {
|
|
6913
|
+
// Initialize the web-specific chapter manager (for UI controls)
|
|
6914
|
+
this.chapterManager = new ChapterManager(
|
|
6915
|
+
this.playerWrapper,
|
|
6916
|
+
this.video,
|
|
6917
|
+
this.chapterConfig
|
|
6918
|
+
);
|
|
6919
|
+
|
|
6920
|
+
// Initialize the core chapter manager (for basic chapter functionality)
|
|
6921
|
+
const coreChapterConfig = {
|
|
6922
|
+
enabled: this.chapterConfig.enabled,
|
|
6923
|
+
chapters: this.convertToChapters(this.chapterConfig.data),
|
|
6924
|
+
segments: this.convertToChapterSegments(this.chapterConfig.data),
|
|
6925
|
+
dataUrl: this.chapterConfig.dataUrl,
|
|
6926
|
+
autoSkip: this.chapterConfig.userPreferences?.autoSkipIntro || false,
|
|
6927
|
+
onChapterChange: (chapter: Chapter | null) => {
|
|
6928
|
+
this.debugLog('Core chapter changed:', chapter?.title || 'none');
|
|
6929
|
+
this.emit('chapterchange', chapter);
|
|
6930
|
+
},
|
|
6931
|
+
onSegmentEntered: (segment: ChapterSegment) => {
|
|
6932
|
+
this.debugLog('Core segment entered:', segment.title);
|
|
6933
|
+
this.emit('segmententered', segment);
|
|
6934
|
+
},
|
|
6935
|
+
onSegmentExited: (segment: ChapterSegment) => {
|
|
6936
|
+
this.debugLog('Core segment exited:', segment.title);
|
|
6937
|
+
this.emit('segmentexited', segment);
|
|
6938
|
+
},
|
|
6939
|
+
onSegmentSkipped: (segment: ChapterSegment) => {
|
|
6940
|
+
this.debugLog('Core segment skipped:', segment.title);
|
|
6941
|
+
this.emit('segmentskipped', segment);
|
|
6942
|
+
}
|
|
6943
|
+
};
|
|
6944
|
+
|
|
6945
|
+
this.coreChapterManager = new CoreChapterManager(coreChapterConfig);
|
|
6946
|
+
|
|
6947
|
+
// Initialize the core chapter manager
|
|
6948
|
+
this.coreChapterManager.initialize();
|
|
6949
|
+
|
|
6950
|
+
// Set up event listeners for web chapter events
|
|
6951
|
+
this.chapterManager.on('segmentEntered', (data) => {
|
|
6952
|
+
this.debugLog('Entered segment:', data.segment.type, data.segment.title);
|
|
6953
|
+
this.emit('chapterSegmentEntered', data);
|
|
6954
|
+
});
|
|
6955
|
+
|
|
6956
|
+
this.chapterManager.on('segmentSkipped', (data) => {
|
|
6957
|
+
this.debugLog('Skipped segment:', data.fromSegment.type, 'to', data.toSegment?.type || 'end');
|
|
6958
|
+
this.emit('chapterSegmentSkipped', data);
|
|
6959
|
+
});
|
|
6960
|
+
|
|
6961
|
+
this.chapterManager.on('skipButtonShown', (data) => {
|
|
6962
|
+
this.debugLog('Skip button shown for:', data.segment.type);
|
|
6963
|
+
this.emit('chapterSkipButtonShown', data);
|
|
6964
|
+
});
|
|
6965
|
+
|
|
6966
|
+
this.chapterManager.on('skipButtonHidden', (data) => {
|
|
6967
|
+
this.debugLog('Skip button hidden for:', data.segment.type, 'reason:', data.reason);
|
|
6968
|
+
this.emit('chapterSkipButtonHidden', data);
|
|
6969
|
+
});
|
|
6970
|
+
|
|
6971
|
+
this.chapterManager.on('chaptersLoaded', (data) => {
|
|
6972
|
+
this.debugLog('Chapters loaded:', data.segmentCount, 'segments');
|
|
6973
|
+
this.emit('chaptersLoaded', data);
|
|
6974
|
+
});
|
|
6975
|
+
|
|
6976
|
+
this.chapterManager.on('chaptersLoadError', (data) => {
|
|
6977
|
+
this.debugError('Failed to load chapters:', data.error.message);
|
|
6978
|
+
this.emit('chaptersLoadError', data);
|
|
6979
|
+
});
|
|
6980
|
+
|
|
6981
|
+
this.debugLog('Chapter managers initialized successfully');
|
|
6982
|
+
} catch (error) {
|
|
6983
|
+
this.debugError('Failed to initialize chapter managers:', error);
|
|
6984
|
+
}
|
|
6985
|
+
}
|
|
6986
|
+
|
|
6987
|
+
/**
|
|
6988
|
+
* Convert web chapter data to core Chapter format
|
|
6989
|
+
*/
|
|
6990
|
+
private convertToChapters(webChapterData: any): Chapter[] {
|
|
6991
|
+
if (!webChapterData || !webChapterData.segments) {
|
|
6992
|
+
return [];
|
|
6993
|
+
}
|
|
6994
|
+
|
|
6995
|
+
return webChapterData.segments
|
|
6996
|
+
.filter((segment: any) => segment.type === 'content')
|
|
6997
|
+
.map((segment: any, index: number) => ({
|
|
6998
|
+
id: segment.id || `chapter-${index}`,
|
|
6999
|
+
title: segment.title || `Chapter ${index + 1}`,
|
|
7000
|
+
startTime: segment.startTime,
|
|
7001
|
+
endTime: segment.endTime,
|
|
7002
|
+
thumbnail: segment.thumbnail,
|
|
7003
|
+
description: segment.description,
|
|
7004
|
+
metadata: segment.metadata || {}
|
|
7005
|
+
}));
|
|
7006
|
+
}
|
|
7007
|
+
|
|
7008
|
+
/**
|
|
7009
|
+
* Convert web chapter data to core ChapterSegment format
|
|
7010
|
+
*/
|
|
7011
|
+
private convertToChapterSegments(webChapterData: any): ChapterSegment[] {
|
|
7012
|
+
if (!webChapterData || !webChapterData.segments) {
|
|
7013
|
+
return [];
|
|
7014
|
+
}
|
|
7015
|
+
|
|
7016
|
+
return webChapterData.segments
|
|
7017
|
+
.filter((segment: any) => segment.type !== 'content')
|
|
7018
|
+
.map((segment: any) => ({
|
|
7019
|
+
id: segment.id,
|
|
7020
|
+
startTime: segment.startTime,
|
|
7021
|
+
endTime: segment.endTime,
|
|
7022
|
+
category: segment.type,
|
|
7023
|
+
action: this.mapSegmentAction(segment.type),
|
|
7024
|
+
title: segment.title,
|
|
7025
|
+
description: segment.description
|
|
7026
|
+
}));
|
|
7027
|
+
}
|
|
7028
|
+
|
|
7029
|
+
/**
|
|
7030
|
+
* Map web segment types to core segment actions
|
|
7031
|
+
*/
|
|
7032
|
+
private mapSegmentAction(segmentType: string): 'skip' | 'mute' | 'warn' {
|
|
7033
|
+
switch (segmentType) {
|
|
7034
|
+
case 'intro':
|
|
7035
|
+
case 'recap':
|
|
7036
|
+
case 'credits':
|
|
7037
|
+
case 'sponsor':
|
|
7038
|
+
return 'skip';
|
|
7039
|
+
case 'offensive':
|
|
7040
|
+
return 'mute';
|
|
7041
|
+
default:
|
|
7042
|
+
return 'warn';
|
|
7043
|
+
}
|
|
7044
|
+
}
|
|
7045
|
+
|
|
7046
|
+
/**
|
|
7047
|
+
* Load chapters data
|
|
7048
|
+
*/
|
|
7049
|
+
public async loadChapters(chapters: VideoChapters): Promise<void> {
|
|
7050
|
+
if (!this.chapterManager) {
|
|
7051
|
+
throw new Error('Chapter manager not initialized. Enable chapters in config first.');
|
|
7052
|
+
}
|
|
7053
|
+
|
|
7054
|
+
try {
|
|
7055
|
+
await this.chapterManager.loadChapters(chapters);
|
|
7056
|
+
this.debugLog('Chapters loaded successfully');
|
|
7057
|
+
} catch (error) {
|
|
7058
|
+
this.debugError('Failed to load chapters:', error);
|
|
7059
|
+
throw error;
|
|
7060
|
+
}
|
|
7061
|
+
}
|
|
7062
|
+
|
|
7063
|
+
/**
|
|
7064
|
+
* Load chapters from URL
|
|
7065
|
+
*/
|
|
7066
|
+
public async loadChaptersFromUrl(url: string): Promise<void> {
|
|
7067
|
+
if (!this.chapterManager) {
|
|
7068
|
+
throw new Error('Chapter manager not initialized. Enable chapters in config first.');
|
|
7069
|
+
}
|
|
7070
|
+
|
|
7071
|
+
try {
|
|
7072
|
+
await this.chapterManager.loadChaptersFromUrl(url);
|
|
7073
|
+
this.debugLog('Chapters loaded from URL successfully');
|
|
7074
|
+
} catch (error) {
|
|
7075
|
+
this.debugError('Failed to load chapters from URL:', error);
|
|
7076
|
+
throw error;
|
|
7077
|
+
}
|
|
7078
|
+
}
|
|
7079
|
+
|
|
7080
|
+
/**
|
|
7081
|
+
* Get current video segment
|
|
7082
|
+
*/
|
|
7083
|
+
public getCurrentSegment(): VideoSegment | null {
|
|
7084
|
+
if (!this.chapterManager || !this.video) {
|
|
7085
|
+
return null;
|
|
7086
|
+
}
|
|
7087
|
+
|
|
7088
|
+
return this.chapterManager.getCurrentSegment(this.video.currentTime);
|
|
7089
|
+
}
|
|
7090
|
+
|
|
7091
|
+
/**
|
|
7092
|
+
* Skip to specific segment by ID
|
|
7093
|
+
*/
|
|
7094
|
+
public skipToSegment(segmentId: string): void {
|
|
7095
|
+
if (!this.chapterManager) {
|
|
7096
|
+
this.debugWarn('Cannot skip segment: chapter manager not initialized');
|
|
7097
|
+
return;
|
|
7098
|
+
}
|
|
7099
|
+
|
|
7100
|
+
this.chapterManager.skipToSegment(segmentId);
|
|
7101
|
+
}
|
|
7102
|
+
|
|
7103
|
+
/**
|
|
7104
|
+
* Get all video segments
|
|
7105
|
+
*/
|
|
7106
|
+
public getSegments(): VideoSegment[] {
|
|
7107
|
+
if (!this.chapterManager) {
|
|
7108
|
+
return [];
|
|
7109
|
+
}
|
|
7110
|
+
|
|
7111
|
+
return this.chapterManager.getSegments();
|
|
7112
|
+
}
|
|
7113
|
+
|
|
7114
|
+
/**
|
|
7115
|
+
* Update chapter configuration
|
|
7116
|
+
*/
|
|
7117
|
+
public updateChapterConfig(newConfig: Partial<ChapterConfig>): void {
|
|
7118
|
+
this.chapterConfig = { ...this.chapterConfig, ...newConfig };
|
|
7119
|
+
|
|
7120
|
+
if (this.chapterManager) {
|
|
7121
|
+
this.chapterManager.updateConfig(this.chapterConfig);
|
|
7122
|
+
}
|
|
7123
|
+
}
|
|
7124
|
+
|
|
7125
|
+
/**
|
|
7126
|
+
* Check if chapters are loaded
|
|
7127
|
+
*/
|
|
7128
|
+
public hasChapters(): boolean {
|
|
7129
|
+
return this.chapterManager?.hasChapters() || false;
|
|
7130
|
+
}
|
|
7131
|
+
|
|
7132
|
+
/**
|
|
7133
|
+
* Get chapter data
|
|
7134
|
+
*/
|
|
7135
|
+
public getChapters(): VideoChapters | null {
|
|
7136
|
+
return this.chapterManager?.getChapters() || null;
|
|
7137
|
+
}
|
|
7138
|
+
|
|
7139
|
+
/**
|
|
7140
|
+
* Get core chapters (Chapter format)
|
|
7141
|
+
*/
|
|
7142
|
+
public getCoreChapters(): Chapter[] {
|
|
7143
|
+
return this.coreChapterManager?.getChapters() || [];
|
|
7144
|
+
}
|
|
7145
|
+
|
|
7146
|
+
/**
|
|
7147
|
+
* Get core chapter segments (ChapterSegment format)
|
|
7148
|
+
*/
|
|
7149
|
+
public getCoreSegments(): ChapterSegment[] {
|
|
7150
|
+
return this.coreChapterManager?.getSegments() || [];
|
|
7151
|
+
}
|
|
7152
|
+
|
|
7153
|
+
/**
|
|
7154
|
+
* Get current chapter info from core manager
|
|
7155
|
+
*/
|
|
7156
|
+
public getCurrentChapterInfo(): Chapter | null {
|
|
7157
|
+
return this.coreChapterManager?.getCurrentChapterInfo() || null;
|
|
7158
|
+
}
|
|
7159
|
+
|
|
7160
|
+
/**
|
|
7161
|
+
* Seek to chapter by ID (core chapter functionality)
|
|
7162
|
+
*/
|
|
7163
|
+
public seekToChapter(chapterId: string): void {
|
|
7164
|
+
if (!this.coreChapterManager || !this.video) {
|
|
7165
|
+
this.debugWarn('Cannot seek to chapter: core chapter manager or video not available');
|
|
7166
|
+
return;
|
|
7167
|
+
}
|
|
7168
|
+
|
|
7169
|
+
const chapter = this.coreChapterManager.seekToChapter(chapterId);
|
|
7170
|
+
if (chapter) {
|
|
7171
|
+
this.video.currentTime = chapter.startTime;
|
|
7172
|
+
this.debugLog('Seeked to chapter:', chapter.title);
|
|
7173
|
+
}
|
|
7174
|
+
}
|
|
7175
|
+
|
|
7176
|
+
/**
|
|
7177
|
+
* Get next chapter from current time
|
|
7178
|
+
*/
|
|
7179
|
+
public getNextChapter(): Chapter | null {
|
|
7180
|
+
if (!this.coreChapterManager || !this.video) {
|
|
7181
|
+
return null;
|
|
7182
|
+
}
|
|
7183
|
+
return this.coreChapterManager.getNextChapter(this.video.currentTime);
|
|
7184
|
+
}
|
|
7185
|
+
|
|
7186
|
+
/**
|
|
7187
|
+
* Get previous chapter from current time
|
|
7188
|
+
*/
|
|
7189
|
+
public getPreviousChapter(): Chapter | null {
|
|
7190
|
+
if (!this.coreChapterManager || !this.video) {
|
|
7191
|
+
return null;
|
|
7192
|
+
}
|
|
7193
|
+
return this.coreChapterManager.getPreviousChapter(this.video.currentTime);
|
|
7194
|
+
}
|
|
7195
|
+
|
|
6622
7196
|
// Theme API: set CSS variables on the wrapper to apply dynamic colors
|
|
6623
7197
|
public setTheme(theme: any): void {
|
|
6624
7198
|
const wrapper = this.playerWrapper;
|
|
@@ -7973,6 +8547,16 @@ export class WebPlayer extends BasePlayer {
|
|
|
7973
8547
|
this.paywallController = null;
|
|
7974
8548
|
}
|
|
7975
8549
|
|
|
8550
|
+
// Destroy chapter managers
|
|
8551
|
+
if (this.chapterManager && typeof this.chapterManager.destroy === 'function') {
|
|
8552
|
+
this.chapterManager.destroy();
|
|
8553
|
+
this.chapterManager = null;
|
|
8554
|
+
}
|
|
8555
|
+
if (this.coreChapterManager) {
|
|
8556
|
+
this.coreChapterManager.destroy();
|
|
8557
|
+
this.coreChapterManager = null;
|
|
8558
|
+
}
|
|
8559
|
+
|
|
7976
8560
|
if (this.video) {
|
|
7977
8561
|
this.video.pause();
|
|
7978
8562
|
this.video.removeAttribute('src');
|