unified-video-framework 1.4.435 → 1.4.437
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/version.d.ts +1 -1
- package/packages/core/dist/version.js +1 -1
- package/packages/core/src/version.ts +1 -1
- package/packages/web/dist/WebPlayer.d.ts +6 -0
- package/packages/web/dist/WebPlayer.d.ts.map +1 -1
- package/packages/web/dist/WebPlayer.js +236 -2
- package/packages/web/dist/WebPlayer.js.map +1 -1
- package/packages/web/dist/drm/systems/WidevineDRM.js +70 -6
- package/packages/web/dist/index.d.ts +1 -0
- package/packages/web/dist/index.d.ts.map +1 -1
- package/packages/web/dist/index.js.map +1 -1
- package/packages/web/dist/react/types/FlashNewsTickerTypes.d.ts +20 -0
- package/packages/web/dist/react/types/FlashNewsTickerTypes.d.ts.map +1 -1
- package/packages/web/src/WebPlayer.ts +280 -4
- package/packages/web/src/index.ts +11 -0
- package/packages/web/src/react/types/FlashNewsTickerTypes.ts +77 -2
|
@@ -5,9 +5,33 @@ import { DRMType as DRMTypeEnum } from "../../../../core/dist/index.js";
|
|
|
5
5
|
export class WidevineDRM extends BaseDRM {
|
|
6
6
|
async initialize() {
|
|
7
7
|
this.log('Initializing Widevine DRM...');
|
|
8
|
+
this.log('Requested robustness - Video:', this.config.videoRobustness || 'SW_SECURE_CRYPTO', 'Audio:', this.config.audioRobustness || 'SW_SECURE_CRYPTO');
|
|
8
9
|
try {
|
|
9
10
|
const keySystemAccess = await navigator.requestMediaKeySystemAccess(this.getKeySystem(), this.getKeySystemConfiguration());
|
|
10
11
|
this.log('Widevine key system access granted');
|
|
12
|
+
|
|
13
|
+
// Log the actual configuration that was accepted
|
|
14
|
+
const actualConfig = keySystemAccess.getConfiguration();
|
|
15
|
+
this.log('✅ GRANTED Configuration:', {
|
|
16
|
+
videoRobustness: actualConfig.videoCapabilities?.[0]?.robustness || 'unknown',
|
|
17
|
+
audioRobustness: actualConfig.audioCapabilities?.[0]?.robustness || 'unknown',
|
|
18
|
+
distinctiveIdentifier: actualConfig.distinctiveIdentifier,
|
|
19
|
+
persistentState: actualConfig.persistentState
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Check if fallback happened
|
|
23
|
+
const requestedVideoRobustness = this.config.videoRobustness || 'SW_SECURE_CRYPTO';
|
|
24
|
+
const grantedVideoRobustness = actualConfig.videoCapabilities?.[0]?.robustness;
|
|
25
|
+
|
|
26
|
+
if (requestedVideoRobustness !== grantedVideoRobustness) {
|
|
27
|
+
this.log('⚠️ WARNING: Browser fell back from', requestedVideoRobustness, 'to', grantedVideoRobustness);
|
|
28
|
+
this.log('⚠️ Screenshot blocking may not work - system does not support hardware overlay');
|
|
29
|
+
} else if (requestedVideoRobustness === 'HW_SECURE_DECODE' && grantedVideoRobustness === 'HW_SECURE_DECODE') {
|
|
30
|
+
this.log('✅ SUCCESS: Hardware overlay protection active - screenshots should show black screen');
|
|
31
|
+
} else {
|
|
32
|
+
this.log('ℹ️ Using software DRM - screenshots will be visible');
|
|
33
|
+
}
|
|
34
|
+
|
|
11
35
|
this.mediaKeys = await keySystemAccess.createMediaKeys();
|
|
12
36
|
}
|
|
13
37
|
catch (error) {
|
|
@@ -22,12 +46,15 @@ export class WidevineDRM extends BaseDRM {
|
|
|
22
46
|
getHLSConfig() {
|
|
23
47
|
this.log('Generating HLS.js config for Widevine');
|
|
24
48
|
const licenseUrl = this.config.licenseUrl || this.config.widevineOptions?.licenseUrl;
|
|
49
|
+
// Use videoRobustness from config if provided, otherwise fallback to SW_SECURE_CRYPTO
|
|
50
|
+
const robustness = this.config.videoRobustness || 'SW_SECURE_CRYPTO';
|
|
51
|
+
this.log(`Using video robustness level: ${robustness}`);
|
|
25
52
|
return {
|
|
26
53
|
emeEnabled: true,
|
|
27
54
|
drmSystems: {
|
|
28
55
|
[this.getKeySystem()]: {
|
|
29
56
|
licenseUrl: licenseUrl,
|
|
30
|
-
robustness:
|
|
57
|
+
robustness: robustness,
|
|
31
58
|
},
|
|
32
59
|
},
|
|
33
60
|
requestMediaKeySystemAccessFunc: navigator.requestMediaKeySystemAccess.bind(navigator),
|
|
@@ -70,12 +97,46 @@ export class WidevineDRM extends BaseDRM {
|
|
|
70
97
|
return protectionData;
|
|
71
98
|
}
|
|
72
99
|
getKeySystemConfiguration() {
|
|
73
|
-
|
|
100
|
+
// Use robustness from config or fallback to SW_SECURE_CRYPTO (L3)
|
|
101
|
+
const videoRobustness = this.config.videoRobustness || 'SW_SECURE_CRYPTO';
|
|
102
|
+
const audioRobustness = this.config.audioRobustness || 'SW_SECURE_CRYPTO';
|
|
103
|
+
|
|
104
|
+
this.log(`Key system config - Video: ${videoRobustness}, Audio: ${audioRobustness}`);
|
|
105
|
+
|
|
106
|
+
// Create configuration with multiple robustness fallbacks
|
|
107
|
+
const configs = [];
|
|
108
|
+
|
|
109
|
+
// Try requested robustness first
|
|
110
|
+
configs.push({
|
|
111
|
+
initDataTypes: ['cenc'],
|
|
112
|
+
audioCapabilities: [{
|
|
113
|
+
contentType: 'audio/mp4; codecs="mp4a.40.2"',
|
|
114
|
+
robustness: audioRobustness,
|
|
115
|
+
}],
|
|
116
|
+
videoCapabilities: [
|
|
117
|
+
{
|
|
118
|
+
contentType: 'video/mp4; codecs="avc1.42E01E"',
|
|
119
|
+
robustness: videoRobustness,
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
contentType: 'video/mp4; codecs="avc1.4d401f"',
|
|
123
|
+
robustness: videoRobustness,
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
distinctiveIdentifier: 'optional',
|
|
127
|
+
persistentState: 'optional',
|
|
128
|
+
sessionTypes: ['temporary'],
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Fallback to SW_SECURE_CRYPTO if hardware not available
|
|
132
|
+
if (videoRobustness !== 'SW_SECURE_CRYPTO') {
|
|
133
|
+
this.log('Adding fallback configuration with SW_SECURE_CRYPTO');
|
|
134
|
+
configs.push({
|
|
74
135
|
initDataTypes: ['cenc'],
|
|
75
136
|
audioCapabilities: [{
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
137
|
+
contentType: 'audio/mp4; codecs="mp4a.40.2"',
|
|
138
|
+
robustness: 'SW_SECURE_CRYPTO',
|
|
139
|
+
}],
|
|
79
140
|
videoCapabilities: [
|
|
80
141
|
{
|
|
81
142
|
contentType: 'video/mp4; codecs="avc1.42E01E"',
|
|
@@ -89,7 +150,10 @@ export class WidevineDRM extends BaseDRM {
|
|
|
89
150
|
distinctiveIdentifier: 'not-allowed',
|
|
90
151
|
persistentState: 'not-allowed',
|
|
91
152
|
sessionTypes: ['temporary'],
|
|
92
|
-
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return configs;
|
|
93
157
|
}
|
|
94
158
|
}
|
|
95
159
|
//# sourceMappingURL=WidevineDRM.js.map
|
|
@@ -2,5 +2,6 @@ export * from '../../core/dist/index';
|
|
|
2
2
|
export { WebPlayer } from './WebPlayer';
|
|
3
3
|
export { WebPlayerView } from './react/WebPlayerView';
|
|
4
4
|
export * from './react/EPG';
|
|
5
|
+
export type { FlashNewsTickerConfig, FlashNewsTickerItem, FlashNewsTickerAPI, TickerDisplayConfig, TickerStyleVariant, BroadcastTheme, BroadcastStyleConfig } from './react/types/FlashNewsTickerTypes';
|
|
5
6
|
export declare const VERSION = "1.0.0";
|
|
6
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,cAAc,uBAAuB,CAAC;AAGtC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAGtD,cAAc,aAAa,CAAC;AAG5B,eAAO,MAAM,OAAO,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,cAAc,uBAAuB,CAAC;AAGtC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAGtD,cAAc,aAAa,CAAC;AAG5B,YAAY,EACV,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,oBAAoB,EACrB,MAAM,oCAAoC,CAAC;AAG5C,eAAO,MAAM,OAAO,UAAU,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,cAAc,uBAAuB,CAAC;AAGtC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAGtD,cAAc,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,cAAc,uBAAuB,CAAC;AAGtC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAGtD,cAAc,aAAa,CAAC;AAc5B,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC"}
|
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
export type TickerStyleVariant = 'simple' | 'broadcast';
|
|
2
|
+
export type BroadcastTheme = 'breaking-red' | 'breaking-blue' | 'alert-red' | 'news-blue' | 'custom';
|
|
3
|
+
export interface BroadcastStyleConfig {
|
|
4
|
+
theme?: BroadcastTheme;
|
|
5
|
+
headerText?: string;
|
|
6
|
+
showGlobe?: boolean;
|
|
7
|
+
showLiveBadge?: boolean;
|
|
8
|
+
headerColor?: string;
|
|
9
|
+
headerTextColor?: string;
|
|
10
|
+
bodyColor?: string;
|
|
11
|
+
headerHeight?: number;
|
|
12
|
+
headerFontSize?: number;
|
|
13
|
+
subHeaderText?: string;
|
|
14
|
+
animateGlobe?: boolean;
|
|
15
|
+
pulseLiveBadge?: boolean;
|
|
16
|
+
}
|
|
1
17
|
export interface FlashNewsTickerItem {
|
|
2
18
|
id: string;
|
|
3
19
|
text: string;
|
|
@@ -18,11 +34,15 @@ export interface TickerDisplayConfig {
|
|
|
18
34
|
gap?: number;
|
|
19
35
|
separator?: string;
|
|
20
36
|
offset?: number;
|
|
37
|
+
styleVariant?: TickerStyleVariant;
|
|
38
|
+
broadcastStyle?: BroadcastStyleConfig;
|
|
21
39
|
}
|
|
22
40
|
export interface FlashNewsTickerConfig {
|
|
23
41
|
enabled?: boolean;
|
|
24
42
|
items?: FlashNewsTickerItem[];
|
|
25
43
|
position?: 'top' | 'bottom' | 'both';
|
|
44
|
+
styleVariant?: TickerStyleVariant;
|
|
45
|
+
broadcastStyle?: BroadcastStyleConfig;
|
|
26
46
|
topConfig?: TickerDisplayConfig;
|
|
27
47
|
bottomConfig?: TickerDisplayConfig;
|
|
28
48
|
height?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FlashNewsTickerTypes.d.ts","sourceRoot":"","sources":["../../../src/react/types/FlashNewsTickerTypes.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FlashNewsTickerTypes.d.ts","sourceRoot":"","sources":["../../../src/react/types/FlashNewsTickerTypes.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
VideoSegment,
|
|
21
21
|
ChapterEvents
|
|
22
22
|
} from './chapters/types/ChapterTypes';
|
|
23
|
-
import type { FlashNewsTickerConfig } from './react/types/FlashNewsTickerTypes';
|
|
23
|
+
import type { FlashNewsTickerConfig, BroadcastStyleConfig, BroadcastTheme, TickerStyleVariant } from './react/types/FlashNewsTickerTypes';
|
|
24
24
|
import YouTubeExtractor from './utils/YouTubeExtractor';
|
|
25
25
|
import { DRMManager, DRMErrorHandler } from './drm';
|
|
26
26
|
import type { DRMInitResult, DRMError } from './drm';
|
|
@@ -2468,8 +2468,18 @@ export class WebPlayer extends BasePlayer {
|
|
|
2468
2468
|
}
|
|
2469
2469
|
|
|
2470
2470
|
private createTickerElement(config: FlashNewsTickerConfig, position: 'top' | 'bottom'): HTMLDivElement {
|
|
2471
|
+
// Route to broadcast style if configured
|
|
2472
|
+
if (config.styleVariant === 'broadcast') {
|
|
2473
|
+
return this.createBroadcastTickerElement(config, position);
|
|
2474
|
+
}
|
|
2475
|
+
|
|
2476
|
+
// Simple style (original implementation)
|
|
2477
|
+
return this.createSimpleTickerElement(config, position);
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
private createSimpleTickerElement(config: FlashNewsTickerConfig, position: 'top' | 'bottom'): HTMLDivElement {
|
|
2471
2481
|
const ticker = document.createElement('div');
|
|
2472
|
-
ticker.className = `uvf-flash-ticker ticker-${position}`;
|
|
2482
|
+
ticker.className = `uvf-flash-ticker ticker-${position} ticker-simple`;
|
|
2473
2483
|
|
|
2474
2484
|
const bottomOffset = config.bottomOffset || 0;
|
|
2475
2485
|
const topOffset = config.topOffset || 0;
|
|
@@ -2567,6 +2577,262 @@ export class WebPlayer extends BasePlayer {
|
|
|
2567
2577
|
ticker.appendChild(track);
|
|
2568
2578
|
|
|
2569
2579
|
// Add animation keyframes to document if not exists
|
|
2580
|
+
this.ensureTickerAnimations();
|
|
2581
|
+
|
|
2582
|
+
return ticker;
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2585
|
+
private createBroadcastTickerElement(config: FlashNewsTickerConfig, position: 'top' | 'bottom'): HTMLDivElement {
|
|
2586
|
+
const broadcastStyle = config.broadcastStyle || {};
|
|
2587
|
+
const theme = broadcastStyle.theme || 'breaking-red';
|
|
2588
|
+
|
|
2589
|
+
// Get theme colors
|
|
2590
|
+
const themeColors = this.getBroadcastThemeColors(theme, broadcastStyle);
|
|
2591
|
+
|
|
2592
|
+
const ticker = document.createElement('div');
|
|
2593
|
+
ticker.className = `uvf-flash-ticker ticker-${position} ticker-broadcast`;
|
|
2594
|
+
|
|
2595
|
+
const bottomOffset = config.bottomOffset || 0;
|
|
2596
|
+
const topOffset = config.topOffset || 0;
|
|
2597
|
+
const headerHeight = broadcastStyle.headerHeight || 28;
|
|
2598
|
+
const bodyHeight = config.height || 36;
|
|
2599
|
+
const totalHeight = headerHeight + bodyHeight;
|
|
2600
|
+
|
|
2601
|
+
ticker.style.cssText = `
|
|
2602
|
+
position: absolute;
|
|
2603
|
+
left: 0;
|
|
2604
|
+
right: 0;
|
|
2605
|
+
height: ${totalHeight}px;
|
|
2606
|
+
${position === 'top' ? `top: ${topOffset}px;` : `bottom: ${bottomOffset}px;`}
|
|
2607
|
+
overflow: hidden;
|
|
2608
|
+
pointer-events: none;
|
|
2609
|
+
display: flex;
|
|
2610
|
+
flex-direction: column;
|
|
2611
|
+
`;
|
|
2612
|
+
|
|
2613
|
+
// Create header row (BREAKING NEWS with globe and LIVE badge)
|
|
2614
|
+
const header = document.createElement('div');
|
|
2615
|
+
header.className = 'uvf-ticker-header';
|
|
2616
|
+
header.style.cssText = `
|
|
2617
|
+
display: flex;
|
|
2618
|
+
align-items: center;
|
|
2619
|
+
height: ${headerHeight}px;
|
|
2620
|
+
background: ${themeColors.headerBg};
|
|
2621
|
+
padding: 0 12px;
|
|
2622
|
+
position: relative;
|
|
2623
|
+
`;
|
|
2624
|
+
|
|
2625
|
+
// Add globe graphic if enabled
|
|
2626
|
+
if (broadcastStyle.showGlobe !== false) {
|
|
2627
|
+
const globe = this.createGlobeElement(broadcastStyle.animateGlobe !== false);
|
|
2628
|
+
header.appendChild(globe);
|
|
2629
|
+
}
|
|
2630
|
+
|
|
2631
|
+
// Add header text (BREAKING NEWS)
|
|
2632
|
+
const headerText = document.createElement('span');
|
|
2633
|
+
headerText.className = 'uvf-ticker-header-text';
|
|
2634
|
+
headerText.textContent = broadcastStyle.headerText || 'BREAKING NEWS';
|
|
2635
|
+
headerText.style.cssText = `
|
|
2636
|
+
color: ${broadcastStyle.headerTextColor || '#ffffff'};
|
|
2637
|
+
font-size: ${broadcastStyle.headerFontSize || 16}px;
|
|
2638
|
+
font-weight: 800;
|
|
2639
|
+
text-transform: uppercase;
|
|
2640
|
+
letter-spacing: 1px;
|
|
2641
|
+
margin-left: ${broadcastStyle.showGlobe !== false ? '8px' : '0'};
|
|
2642
|
+
text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
|
|
2643
|
+
`;
|
|
2644
|
+
header.appendChild(headerText);
|
|
2645
|
+
|
|
2646
|
+
// Add LIVE badge if enabled
|
|
2647
|
+
if (broadcastStyle.showLiveBadge !== false) {
|
|
2648
|
+
const liveBadge = this.createLiveBadgeElement(broadcastStyle.pulseLiveBadge !== false);
|
|
2649
|
+
header.appendChild(liveBadge);
|
|
2650
|
+
}
|
|
2651
|
+
|
|
2652
|
+
// Create body row (scrolling ticker)
|
|
2653
|
+
const body = document.createElement('div');
|
|
2654
|
+
body.className = 'uvf-ticker-body';
|
|
2655
|
+
body.style.cssText = `
|
|
2656
|
+
display: flex;
|
|
2657
|
+
align-items: center;
|
|
2658
|
+
height: ${bodyHeight}px;
|
|
2659
|
+
background: ${themeColors.bodyBg};
|
|
2660
|
+
overflow: hidden;
|
|
2661
|
+
position: relative;
|
|
2662
|
+
`;
|
|
2663
|
+
|
|
2664
|
+
// Create scrolling track
|
|
2665
|
+
const track = document.createElement('div');
|
|
2666
|
+
track.className = 'uvf-ticker-track';
|
|
2667
|
+
|
|
2668
|
+
const duration = this.calculateTickerDuration(config);
|
|
2669
|
+
track.style.cssText = `
|
|
2670
|
+
display: flex;
|
|
2671
|
+
white-space: nowrap;
|
|
2672
|
+
animation: ticker-scroll ${duration}s linear infinite;
|
|
2673
|
+
will-change: transform;
|
|
2674
|
+
padding-left: 100%;
|
|
2675
|
+
`;
|
|
2676
|
+
|
|
2677
|
+
// Calculate responsive font size
|
|
2678
|
+
const containerWidth = this.container?.offsetWidth || 1920;
|
|
2679
|
+
const baseFontSize = config.fontSize || 14;
|
|
2680
|
+
let responsiveFontSize = baseFontSize;
|
|
2681
|
+
if (containerWidth < 768) {
|
|
2682
|
+
responsiveFontSize = Math.max(baseFontSize * 0.8, 12);
|
|
2683
|
+
} else if (containerWidth < 1280) {
|
|
2684
|
+
responsiveFontSize = Math.max(baseFontSize * 0.9, 13);
|
|
2685
|
+
}
|
|
2686
|
+
|
|
2687
|
+
// Render items
|
|
2688
|
+
const renderItems = () => {
|
|
2689
|
+
if (!config.items) return;
|
|
2690
|
+
config.items.forEach((item) => {
|
|
2691
|
+
const span = document.createElement('span');
|
|
2692
|
+
if (item.html) {
|
|
2693
|
+
span.innerHTML = item.html;
|
|
2694
|
+
} else {
|
|
2695
|
+
span.textContent = item.text;
|
|
2696
|
+
}
|
|
2697
|
+
span.style.cssText = `
|
|
2698
|
+
color: ${config.textColor || '#ffffff'};
|
|
2699
|
+
font-size: ${responsiveFontSize}px;
|
|
2700
|
+
font-weight: ${config.fontWeight || 600};
|
|
2701
|
+
margin-right: ${config.gap || 100}px;
|
|
2702
|
+
display: inline-flex;
|
|
2703
|
+
align-items: center;
|
|
2704
|
+
`;
|
|
2705
|
+
track.appendChild(span);
|
|
2706
|
+
|
|
2707
|
+
if (config.separator) {
|
|
2708
|
+
const sep = document.createElement('span');
|
|
2709
|
+
sep.textContent = config.separator;
|
|
2710
|
+
sep.style.cssText = `
|
|
2711
|
+
color: ${config.textColor || '#ffffff'};
|
|
2712
|
+
font-size: ${responsiveFontSize}px;
|
|
2713
|
+
opacity: 0.5;
|
|
2714
|
+
margin: 0 8px;
|
|
2715
|
+
`;
|
|
2716
|
+
track.appendChild(sep);
|
|
2717
|
+
}
|
|
2718
|
+
});
|
|
2719
|
+
};
|
|
2720
|
+
|
|
2721
|
+
for (let i = 0; i < 10; i++) {
|
|
2722
|
+
renderItems();
|
|
2723
|
+
}
|
|
2724
|
+
|
|
2725
|
+
body.appendChild(track);
|
|
2726
|
+
ticker.appendChild(header);
|
|
2727
|
+
ticker.appendChild(body);
|
|
2728
|
+
|
|
2729
|
+
// Add all animation keyframes
|
|
2730
|
+
this.ensureTickerAnimations();
|
|
2731
|
+
|
|
2732
|
+
return ticker;
|
|
2733
|
+
}
|
|
2734
|
+
|
|
2735
|
+
private getBroadcastThemeColors(theme: BroadcastTheme, broadcastStyle: BroadcastStyleConfig): { headerBg: string; bodyBg: string } {
|
|
2736
|
+
switch (theme) {
|
|
2737
|
+
case 'breaking-red':
|
|
2738
|
+
return {
|
|
2739
|
+
headerBg: 'linear-gradient(90deg, #cc0000 0%, #ff0000 50%, #cc0000 100%)',
|
|
2740
|
+
bodyBg: 'linear-gradient(90deg, #1a237e 0%, #283593 50%, #1a237e 100%)'
|
|
2741
|
+
};
|
|
2742
|
+
case 'breaking-blue':
|
|
2743
|
+
return {
|
|
2744
|
+
headerBg: 'linear-gradient(90deg, #0d47a1 0%, #1565c0 50%, #0d47a1 100%)',
|
|
2745
|
+
bodyBg: 'linear-gradient(90deg, #1a1a2e 0%, #16213e 50%, #1a1a2e 100%)'
|
|
2746
|
+
};
|
|
2747
|
+
case 'alert-red':
|
|
2748
|
+
return {
|
|
2749
|
+
headerBg: 'linear-gradient(90deg, #b71c1c 0%, #d32f2f 50%, #b71c1c 100%)',
|
|
2750
|
+
bodyBg: 'linear-gradient(90deg, #7f0000 0%, #9a0000 50%, #7f0000 100%)'
|
|
2751
|
+
};
|
|
2752
|
+
case 'news-blue':
|
|
2753
|
+
return {
|
|
2754
|
+
headerBg: 'linear-gradient(90deg, #01579b 0%, #0277bd 50%, #01579b 100%)',
|
|
2755
|
+
bodyBg: 'linear-gradient(90deg, #002171 0%, #003c8f 50%, #002171 100%)'
|
|
2756
|
+
};
|
|
2757
|
+
case 'custom':
|
|
2758
|
+
return {
|
|
2759
|
+
headerBg: broadcastStyle.headerColor || '#cc0000',
|
|
2760
|
+
bodyBg: broadcastStyle.bodyColor || '#1a237e'
|
|
2761
|
+
};
|
|
2762
|
+
default:
|
|
2763
|
+
return {
|
|
2764
|
+
headerBg: 'linear-gradient(90deg, #cc0000 0%, #ff0000 50%, #cc0000 100%)',
|
|
2765
|
+
bodyBg: 'linear-gradient(90deg, #1a237e 0%, #283593 50%, #1a237e 100%)'
|
|
2766
|
+
};
|
|
2767
|
+
}
|
|
2768
|
+
}
|
|
2769
|
+
|
|
2770
|
+
private createGlobeElement(animate: boolean): HTMLDivElement {
|
|
2771
|
+
const globeContainer = document.createElement('div');
|
|
2772
|
+
globeContainer.className = 'uvf-ticker-globe';
|
|
2773
|
+
globeContainer.style.cssText = `
|
|
2774
|
+
width: 24px;
|
|
2775
|
+
height: 24px;
|
|
2776
|
+
position: relative;
|
|
2777
|
+
flex-shrink: 0;
|
|
2778
|
+
`;
|
|
2779
|
+
|
|
2780
|
+
// SVG Globe icon
|
|
2781
|
+
globeContainer.innerHTML = `
|
|
2782
|
+
<svg viewBox="0 0 24 24" fill="none" style="width: 100%; height: 100%; ${animate ? 'animation: globe-rotate 8s linear infinite;' : ''}">
|
|
2783
|
+
<circle cx="12" cy="12" r="10" stroke="#ffffff" stroke-width="1.5" fill="none"/>
|
|
2784
|
+
<ellipse cx="12" cy="12" rx="10" ry="4" stroke="#ffffff" stroke-width="1" fill="none"/>
|
|
2785
|
+
<ellipse cx="12" cy="12" rx="4" ry="10" stroke="#ffffff" stroke-width="1" fill="none"/>
|
|
2786
|
+
<line x1="2" y1="12" x2="22" y2="12" stroke="#ffffff" stroke-width="0.5"/>
|
|
2787
|
+
<line x1="12" y1="2" x2="12" y2="22" stroke="#ffffff" stroke-width="0.5"/>
|
|
2788
|
+
<path d="M4 8 Q12 6 20 8" stroke="#ffffff" stroke-width="0.5" fill="none"/>
|
|
2789
|
+
<path d="M4 16 Q12 18 20 16" stroke="#ffffff" stroke-width="0.5" fill="none"/>
|
|
2790
|
+
</svg>
|
|
2791
|
+
`;
|
|
2792
|
+
|
|
2793
|
+
return globeContainer;
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2796
|
+
private createLiveBadgeElement(pulse: boolean): HTMLDivElement {
|
|
2797
|
+
const badge = document.createElement('div');
|
|
2798
|
+
badge.className = 'uvf-ticker-live-badge';
|
|
2799
|
+
badge.style.cssText = `
|
|
2800
|
+
display: flex;
|
|
2801
|
+
align-items: center;
|
|
2802
|
+
margin-left: auto;
|
|
2803
|
+
padding: 2px 8px;
|
|
2804
|
+
background: #ff0000;
|
|
2805
|
+
border-radius: 3px;
|
|
2806
|
+
${pulse ? 'animation: live-pulse 1.5s ease-in-out infinite;' : ''}
|
|
2807
|
+
`;
|
|
2808
|
+
|
|
2809
|
+
// Red dot
|
|
2810
|
+
const dot = document.createElement('span');
|
|
2811
|
+
dot.style.cssText = `
|
|
2812
|
+
width: 8px;
|
|
2813
|
+
height: 8px;
|
|
2814
|
+
background: #ffffff;
|
|
2815
|
+
border-radius: 50%;
|
|
2816
|
+
margin-right: 4px;
|
|
2817
|
+
${pulse ? 'animation: dot-blink 1s ease-in-out infinite;' : ''}
|
|
2818
|
+
`;
|
|
2819
|
+
badge.appendChild(dot);
|
|
2820
|
+
|
|
2821
|
+
// LIVE text
|
|
2822
|
+
const text = document.createElement('span');
|
|
2823
|
+
text.textContent = 'LIVE';
|
|
2824
|
+
text.style.cssText = `
|
|
2825
|
+
color: #ffffff;
|
|
2826
|
+
font-size: 11px;
|
|
2827
|
+
font-weight: 800;
|
|
2828
|
+
letter-spacing: 0.5px;
|
|
2829
|
+
`;
|
|
2830
|
+
badge.appendChild(text);
|
|
2831
|
+
|
|
2832
|
+
return badge;
|
|
2833
|
+
}
|
|
2834
|
+
|
|
2835
|
+
private ensureTickerAnimations(): void {
|
|
2570
2836
|
if (!document.querySelector('#uvf-ticker-animation')) {
|
|
2571
2837
|
const style = document.createElement('style');
|
|
2572
2838
|
style.id = 'uvf-ticker-animation';
|
|
@@ -2575,11 +2841,21 @@ export class WebPlayer extends BasePlayer {
|
|
|
2575
2841
|
0% { transform: translateX(0%); }
|
|
2576
2842
|
100% { transform: translateX(-100%); }
|
|
2577
2843
|
}
|
|
2844
|
+
@keyframes globe-rotate {
|
|
2845
|
+
0% { transform: rotate(0deg); }
|
|
2846
|
+
100% { transform: rotate(360deg); }
|
|
2847
|
+
}
|
|
2848
|
+
@keyframes live-pulse {
|
|
2849
|
+
0%, 100% { opacity: 1; transform: scale(1); }
|
|
2850
|
+
50% { opacity: 0.85; transform: scale(1.02); }
|
|
2851
|
+
}
|
|
2852
|
+
@keyframes dot-blink {
|
|
2853
|
+
0%, 100% { opacity: 1; }
|
|
2854
|
+
50% { opacity: 0.3; }
|
|
2855
|
+
}
|
|
2578
2856
|
`;
|
|
2579
2857
|
document.head.appendChild(style);
|
|
2580
2858
|
}
|
|
2581
|
-
|
|
2582
|
-
return ticker;
|
|
2583
2859
|
}
|
|
2584
2860
|
|
|
2585
2861
|
private calculateTickerDuration(config: FlashNewsTickerConfig): number {
|
|
@@ -13,5 +13,16 @@ export { WebPlayerView } from './react/WebPlayerView';
|
|
|
13
13
|
// Export EPG (Electronic Program Guide) components
|
|
14
14
|
export * from './react/EPG';
|
|
15
15
|
|
|
16
|
+
// Export Flash News Ticker types
|
|
17
|
+
export type {
|
|
18
|
+
FlashNewsTickerConfig,
|
|
19
|
+
FlashNewsTickerItem,
|
|
20
|
+
FlashNewsTickerAPI,
|
|
21
|
+
TickerDisplayConfig,
|
|
22
|
+
TickerStyleVariant,
|
|
23
|
+
BroadcastTheme,
|
|
24
|
+
BroadcastStyleConfig
|
|
25
|
+
} from './react/types/FlashNewsTickerTypes';
|
|
26
|
+
|
|
16
27
|
// Version
|
|
17
28
|
export const VERSION = '1.0.0';
|
|
@@ -2,9 +2,68 @@
|
|
|
2
2
|
* Flash News Ticker Types
|
|
3
3
|
*
|
|
4
4
|
* Type definitions for the flash news ticker feature that displays
|
|
5
|
-
* scrolling text overlays on the video player.
|
|
5
|
+
* scrolling text overlays on the video player. Supports both simple
|
|
6
|
+
* tickers and broadcast-style breaking news banners.
|
|
6
7
|
*/
|
|
7
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Style variant for the ticker
|
|
11
|
+
* - 'simple': Basic scrolling ticker with single background
|
|
12
|
+
* - 'broadcast': Professional broadcast-style with header, body, globe, and LIVE badge
|
|
13
|
+
*/
|
|
14
|
+
export type TickerStyleVariant = 'simple' | 'broadcast';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Broadcast style theme presets
|
|
18
|
+
*/
|
|
19
|
+
export type BroadcastTheme =
|
|
20
|
+
| 'breaking-red' // Red header with blue body (classic breaking news)
|
|
21
|
+
| 'breaking-blue' // Blue header with dark body
|
|
22
|
+
| 'alert-red' // Full red theme for urgent alerts
|
|
23
|
+
| 'news-blue' // Professional blue theme
|
|
24
|
+
| 'custom'; // Custom colors via headerColor/bodyColor
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Configuration for broadcast-style banner appearance
|
|
28
|
+
*/
|
|
29
|
+
export interface BroadcastStyleConfig {
|
|
30
|
+
/** Theme preset. Default: 'breaking-red' */
|
|
31
|
+
theme?: BroadcastTheme;
|
|
32
|
+
|
|
33
|
+
/** Header text (e.g., "BREAKING NEWS", "LIVE", "ALERT"). Default: 'BREAKING NEWS' */
|
|
34
|
+
headerText?: string;
|
|
35
|
+
|
|
36
|
+
/** Show the globe/world graphic on the left. Default: true */
|
|
37
|
+
showGlobe?: boolean;
|
|
38
|
+
|
|
39
|
+
/** Show LIVE badge indicator. Default: true */
|
|
40
|
+
showLiveBadge?: boolean;
|
|
41
|
+
|
|
42
|
+
/** Custom header background color (only used when theme is 'custom') */
|
|
43
|
+
headerColor?: string;
|
|
44
|
+
|
|
45
|
+
/** Custom header text color. Default: '#ffffff' */
|
|
46
|
+
headerTextColor?: string;
|
|
47
|
+
|
|
48
|
+
/** Custom body/ticker background color (only used when theme is 'custom') */
|
|
49
|
+
bodyColor?: string;
|
|
50
|
+
|
|
51
|
+
/** Header height in pixels. Default: 28 */
|
|
52
|
+
headerHeight?: number;
|
|
53
|
+
|
|
54
|
+
/** Header font size in pixels. Default: 16 */
|
|
55
|
+
headerFontSize?: number;
|
|
56
|
+
|
|
57
|
+
/** Secondary text shown below header (optional) */
|
|
58
|
+
subHeaderText?: string;
|
|
59
|
+
|
|
60
|
+
/** Globe animation enabled. Default: true */
|
|
61
|
+
animateGlobe?: boolean;
|
|
62
|
+
|
|
63
|
+
/** LIVE badge pulse animation. Default: true */
|
|
64
|
+
pulseLiveBadge?: boolean;
|
|
65
|
+
}
|
|
66
|
+
|
|
8
67
|
/**
|
|
9
68
|
* Individual news item to display in the ticker
|
|
10
69
|
*/
|
|
@@ -44,7 +103,7 @@ export interface TickerDisplayConfig {
|
|
|
44
103
|
|
|
45
104
|
// Visual styling
|
|
46
105
|
|
|
47
|
-
/** Height of ticker in pixels. Default: 40 */
|
|
106
|
+
/** Height of ticker in pixels. Default: 40 for simple, 60 for broadcast */
|
|
48
107
|
height?: number;
|
|
49
108
|
|
|
50
109
|
/** Background color with alpha. Default: 'rgba(0,0,0,0.7)' */
|
|
@@ -72,6 +131,12 @@ export interface TickerDisplayConfig {
|
|
|
72
131
|
|
|
73
132
|
/** Offset in pixels for safe area. Default: 0 for bottom, 10 for top */
|
|
74
133
|
offset?: number;
|
|
134
|
+
|
|
135
|
+
/** Style variant for this specific ticker. Overrides parent styleVariant */
|
|
136
|
+
styleVariant?: TickerStyleVariant;
|
|
137
|
+
|
|
138
|
+
/** Broadcast style config for this specific ticker. Overrides parent broadcastStyle */
|
|
139
|
+
broadcastStyle?: BroadcastStyleConfig;
|
|
75
140
|
}
|
|
76
141
|
|
|
77
142
|
/**
|
|
@@ -87,6 +152,16 @@ export interface FlashNewsTickerConfig {
|
|
|
87
152
|
/** Position on screen. Default: 'bottom' */
|
|
88
153
|
position?: 'top' | 'bottom' | 'both';
|
|
89
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Style variant for the ticker. Default: 'simple'
|
|
157
|
+
* - 'simple': Basic scrolling ticker (original style)
|
|
158
|
+
* - 'broadcast': Professional broadcast-style breaking news banner
|
|
159
|
+
*/
|
|
160
|
+
styleVariant?: TickerStyleVariant;
|
|
161
|
+
|
|
162
|
+
/** Configuration for broadcast-style appearance (only used when styleVariant is 'broadcast') */
|
|
163
|
+
broadcastStyle?: BroadcastStyleConfig;
|
|
164
|
+
|
|
90
165
|
/** Configuration for top ticker (only used when position is 'both') */
|
|
91
166
|
topConfig?: TickerDisplayConfig;
|
|
92
167
|
|