unified-video-framework 1.4.437 → 1.4.439
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 +28 -0
- package/packages/web/dist/WebPlayer.d.ts.map +1 -1
- package/packages/web/dist/WebPlayer.js +679 -8
- package/packages/web/dist/WebPlayer.js.map +1 -1
- 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.map +1 -1
- package/packages/web/dist/react/types/FlashNewsTickerTypes.d.ts +61 -0
- package/packages/web/dist/react/types/FlashNewsTickerTypes.d.ts.map +1 -1
- package/packages/web/src/WebPlayer.ts +912 -9
- package/packages/web/src/index.ts +10 -1
- package/packages/web/src/react/types/FlashNewsTickerTypes.ts +214 -1
|
@@ -34,6 +34,17 @@ export class WebPlayer extends BasePlayer {
|
|
|
34
34
|
this.flashTickerContainer = null;
|
|
35
35
|
this.flashTickerTopElement = null;
|
|
36
36
|
this.flashTickerBottomElement = null;
|
|
37
|
+
this.tickerCurrentItemIndex = 0;
|
|
38
|
+
this.tickerCycleTimer = null;
|
|
39
|
+
this.tickerConfig = null;
|
|
40
|
+
this.tickerHeadlineElement = null;
|
|
41
|
+
this.tickerDetailElement = null;
|
|
42
|
+
this.tickerIntroOverlay = null;
|
|
43
|
+
this.tickerProgressBar = null;
|
|
44
|
+
this.tickerProgressFill = null;
|
|
45
|
+
this.tickerIsPaused = false;
|
|
46
|
+
this.tickerPauseStartTime = 0;
|
|
47
|
+
this.tickerRemainingTime = 0;
|
|
37
48
|
this.previewGateHit = false;
|
|
38
49
|
this.paymentSuccessTime = 0;
|
|
39
50
|
this.paymentSuccessful = false;
|
|
@@ -97,6 +108,8 @@ export class WebPlayer extends BasePlayer {
|
|
|
97
108
|
this.currentRetryAttempt = 0;
|
|
98
109
|
this.lastFailedUrl = '';
|
|
99
110
|
this.isFallbackPosterMode = false;
|
|
111
|
+
this.hlsErrorRetryCount = 0;
|
|
112
|
+
this.MAX_HLS_ERROR_RETRIES = 3;
|
|
100
113
|
this.lastDuration = 0;
|
|
101
114
|
this.isDetectedAsLive = false;
|
|
102
115
|
this.autoplayAttempted = false;
|
|
@@ -531,6 +544,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
531
544
|
this.currentRetryAttempt = 0;
|
|
532
545
|
this.lastFailedUrl = '';
|
|
533
546
|
this.isFallbackPosterMode = false;
|
|
547
|
+
this.hlsErrorRetryCount = 0;
|
|
534
548
|
await this.cleanup();
|
|
535
549
|
if (!this.video) {
|
|
536
550
|
throw new Error('Video element not initialized');
|
|
@@ -828,6 +842,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
828
842
|
this.hls.loadSource(url);
|
|
829
843
|
this.hls.attachMedia(this.video);
|
|
830
844
|
this.hls.on(window.Hls.Events.MANIFEST_PARSED, (event, data) => {
|
|
845
|
+
this.hlsErrorRetryCount = 0;
|
|
831
846
|
this.qualities = data.levels.map((level, index) => ({
|
|
832
847
|
height: level.height,
|
|
833
848
|
width: level.width || 0,
|
|
@@ -861,27 +876,85 @@ export class WebPlayer extends BasePlayer {
|
|
|
861
876
|
throw new Error('HLS is not supported in this browser');
|
|
862
877
|
}
|
|
863
878
|
}
|
|
864
|
-
handleHLSError(data) {
|
|
879
|
+
async handleHLSError(data) {
|
|
865
880
|
const Hls = window.Hls;
|
|
881
|
+
this.debugLog(`🔴 HLS Error: type=${data.type}, details=${data.details}, fatal=${data.fatal}`);
|
|
882
|
+
if (this.hlsErrorRetryCount >= this.MAX_HLS_ERROR_RETRIES) {
|
|
883
|
+
this.debugLog(`🔴 HLS max retries (${this.MAX_HLS_ERROR_RETRIES}) exceeded, triggering fallback`);
|
|
884
|
+
this.hls?.destroy();
|
|
885
|
+
this.hls = null;
|
|
886
|
+
const fallbackLoaded = await this.tryFallbackSource({
|
|
887
|
+
code: 'HLS_ERROR',
|
|
888
|
+
message: data.details,
|
|
889
|
+
type: data.type,
|
|
890
|
+
fatal: true,
|
|
891
|
+
details: data
|
|
892
|
+
});
|
|
893
|
+
if (!fallbackLoaded) {
|
|
894
|
+
this.handleError({
|
|
895
|
+
code: 'HLS_ERROR',
|
|
896
|
+
message: `HLS stream failed after ${this.MAX_HLS_ERROR_RETRIES} retries: ${data.details}`,
|
|
897
|
+
type: 'media',
|
|
898
|
+
fatal: true,
|
|
899
|
+
details: data
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
866
904
|
switch (data.type) {
|
|
867
905
|
case Hls.ErrorTypes.NETWORK_ERROR:
|
|
868
|
-
|
|
869
|
-
this.
|
|
906
|
+
this.hlsErrorRetryCount++;
|
|
907
|
+
this.debugLog(`🔴 Fatal network error (attempt ${this.hlsErrorRetryCount}/${this.MAX_HLS_ERROR_RETRIES}), trying to recover`);
|
|
908
|
+
if (data.details === 'manifestLoadError' || data.details === 'manifestParsingError') {
|
|
909
|
+
this.debugLog(`🔴 Manifest error detected (${data.details}), skipping recovery - triggering fallback`);
|
|
910
|
+
this.hls?.destroy();
|
|
911
|
+
this.hls = null;
|
|
912
|
+
const fallbackLoaded = await this.tryFallbackSource({
|
|
913
|
+
code: 'HLS_MANIFEST_ERROR',
|
|
914
|
+
message: data.details,
|
|
915
|
+
type: 'network',
|
|
916
|
+
fatal: true,
|
|
917
|
+
details: data
|
|
918
|
+
});
|
|
919
|
+
if (!fallbackLoaded) {
|
|
920
|
+
this.handleError({
|
|
921
|
+
code: 'HLS_MANIFEST_ERROR',
|
|
922
|
+
message: `Failed to load HLS manifest: ${data.details}`,
|
|
923
|
+
type: 'media',
|
|
924
|
+
fatal: true,
|
|
925
|
+
details: data
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
else {
|
|
930
|
+
this.hls?.startLoad();
|
|
931
|
+
}
|
|
870
932
|
break;
|
|
871
933
|
case Hls.ErrorTypes.MEDIA_ERROR:
|
|
872
|
-
|
|
873
|
-
this.
|
|
934
|
+
this.hlsErrorRetryCount++;
|
|
935
|
+
this.debugLog(`🔴 Fatal media error (attempt ${this.hlsErrorRetryCount}/${this.MAX_HLS_ERROR_RETRIES}), trying to recover`);
|
|
936
|
+
this.hls?.recoverMediaError();
|
|
874
937
|
break;
|
|
875
938
|
default:
|
|
876
|
-
|
|
877
|
-
this.
|
|
939
|
+
this.debugLog(`🔴 Fatal unrecoverable HLS error: ${data.details}`);
|
|
940
|
+
this.hls?.destroy();
|
|
941
|
+
this.hls = null;
|
|
942
|
+
const fallbackLoaded = await this.tryFallbackSource({
|
|
878
943
|
code: 'HLS_ERROR',
|
|
879
944
|
message: data.details,
|
|
880
945
|
type: 'media',
|
|
881
946
|
fatal: true,
|
|
882
947
|
details: data
|
|
883
948
|
});
|
|
884
|
-
|
|
949
|
+
if (!fallbackLoaded) {
|
|
950
|
+
this.handleError({
|
|
951
|
+
code: 'HLS_ERROR',
|
|
952
|
+
message: data.details,
|
|
953
|
+
type: 'media',
|
|
954
|
+
fatal: true,
|
|
955
|
+
details: data
|
|
956
|
+
});
|
|
957
|
+
}
|
|
885
958
|
break;
|
|
886
959
|
}
|
|
887
960
|
}
|
|
@@ -1877,6 +1950,10 @@ export class WebPlayer extends BasePlayer {
|
|
|
1877
1950
|
}
|
|
1878
1951
|
createTickerElement(config, position) {
|
|
1879
1952
|
if (config.styleVariant === 'broadcast') {
|
|
1953
|
+
const layoutStyle = config.broadcastStyle?.layoutStyle || 'broadcast';
|
|
1954
|
+
if (layoutStyle === 'two-line' || layoutStyle === 'professional') {
|
|
1955
|
+
return this.createProfessionalTickerElement(config, position);
|
|
1956
|
+
}
|
|
1880
1957
|
return this.createBroadcastTickerElement(config, position);
|
|
1881
1958
|
}
|
|
1882
1959
|
return this.createSimpleTickerElement(config, position);
|
|
@@ -2179,14 +2256,19 @@ export class WebPlayer extends BasePlayer {
|
|
|
2179
2256
|
const style = document.createElement('style');
|
|
2180
2257
|
style.id = 'uvf-ticker-animation';
|
|
2181
2258
|
style.textContent = `
|
|
2259
|
+
/* Basic ticker scroll */
|
|
2182
2260
|
@keyframes ticker-scroll {
|
|
2183
2261
|
0% { transform: translateX(0%); }
|
|
2184
2262
|
100% { transform: translateX(-100%); }
|
|
2185
2263
|
}
|
|
2264
|
+
|
|
2265
|
+
/* Globe rotation */
|
|
2186
2266
|
@keyframes globe-rotate {
|
|
2187
2267
|
0% { transform: rotate(0deg); }
|
|
2188
2268
|
100% { transform: rotate(360deg); }
|
|
2189
2269
|
}
|
|
2270
|
+
|
|
2271
|
+
/* LIVE badge animations */
|
|
2190
2272
|
@keyframes live-pulse {
|
|
2191
2273
|
0%, 100% { opacity: 1; transform: scale(1); }
|
|
2192
2274
|
50% { opacity: 0.85; transform: scale(1.02); }
|
|
@@ -2195,6 +2277,85 @@ export class WebPlayer extends BasePlayer {
|
|
|
2195
2277
|
0%, 100% { opacity: 1; }
|
|
2196
2278
|
50% { opacity: 0.3; }
|
|
2197
2279
|
}
|
|
2280
|
+
|
|
2281
|
+
/* Intro Animations */
|
|
2282
|
+
@keyframes intro-slide-in {
|
|
2283
|
+
0% { transform: translateX(-100%); opacity: 0; }
|
|
2284
|
+
20% { transform: translateX(0); opacity: 1; }
|
|
2285
|
+
80% { transform: translateX(0); opacity: 1; }
|
|
2286
|
+
100% { transform: translateX(100%); opacity: 0; }
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2289
|
+
@keyframes intro-flash {
|
|
2290
|
+
0%, 100% { opacity: 0; }
|
|
2291
|
+
10%, 30%, 50%, 70%, 90% { opacity: 1; background: rgba(255,255,255,0.3); }
|
|
2292
|
+
20%, 40%, 60%, 80% { opacity: 1; background: transparent; }
|
|
2293
|
+
}
|
|
2294
|
+
|
|
2295
|
+
@keyframes intro-scale {
|
|
2296
|
+
0% { transform: scale(0); opacity: 0; }
|
|
2297
|
+
20% { transform: scale(1.2); opacity: 1; }
|
|
2298
|
+
30% { transform: scale(1); }
|
|
2299
|
+
80% { transform: scale(1); opacity: 1; }
|
|
2300
|
+
100% { transform: scale(0); opacity: 0; }
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
@keyframes intro-pulse {
|
|
2304
|
+
0% { transform: scale(0.8); opacity: 0; }
|
|
2305
|
+
25% { transform: scale(1.1); opacity: 1; }
|
|
2306
|
+
50% { transform: scale(1); opacity: 1; }
|
|
2307
|
+
75% { transform: scale(1); opacity: 1; }
|
|
2308
|
+
100% { transform: scale(0.8); opacity: 0; }
|
|
2309
|
+
}
|
|
2310
|
+
|
|
2311
|
+
@keyframes intro-shake {
|
|
2312
|
+
0% { transform: translateX(0); opacity: 0; }
|
|
2313
|
+
10% { opacity: 1; }
|
|
2314
|
+
15%, 35%, 55%, 75% { transform: translateX(-5px); }
|
|
2315
|
+
25%, 45%, 65%, 85% { transform: translateX(5px); }
|
|
2316
|
+
90% { transform: translateX(0); opacity: 1; }
|
|
2317
|
+
100% { transform: translateX(0); opacity: 0; }
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2320
|
+
@keyframes intro-none {
|
|
2321
|
+
0% { opacity: 1; }
|
|
2322
|
+
80% { opacity: 1; }
|
|
2323
|
+
100% { opacity: 0; }
|
|
2324
|
+
}
|
|
2325
|
+
|
|
2326
|
+
/* Item Transitions */
|
|
2327
|
+
@keyframes item-fade-out {
|
|
2328
|
+
from { opacity: 1; }
|
|
2329
|
+
to { opacity: 0; }
|
|
2330
|
+
}
|
|
2331
|
+
@keyframes item-fade-in {
|
|
2332
|
+
from { opacity: 0; }
|
|
2333
|
+
to { opacity: 1; }
|
|
2334
|
+
}
|
|
2335
|
+
@keyframes item-slide-out {
|
|
2336
|
+
from { transform: translateY(0); opacity: 1; }
|
|
2337
|
+
to { transform: translateY(-100%); opacity: 0; }
|
|
2338
|
+
}
|
|
2339
|
+
@keyframes item-slide-in {
|
|
2340
|
+
from { transform: translateY(100%); opacity: 0; }
|
|
2341
|
+
to { transform: translateY(0); opacity: 1; }
|
|
2342
|
+
}
|
|
2343
|
+
|
|
2344
|
+
/* Decorative Separator Animations */
|
|
2345
|
+
@keyframes separator-pulse {
|
|
2346
|
+
0%, 100% { opacity: 0.8; transform: scaleY(1); }
|
|
2347
|
+
50% { opacity: 1; transform: scaleY(1.1); }
|
|
2348
|
+
}
|
|
2349
|
+
|
|
2350
|
+
@keyframes chevron-pulse {
|
|
2351
|
+
0%, 100% { transform: translateX(0); opacity: 0.8; }
|
|
2352
|
+
50% { transform: translateX(3px); opacity: 1; }
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2355
|
+
@keyframes diamond-spin {
|
|
2356
|
+
from { transform: rotate(0deg); }
|
|
2357
|
+
to { transform: rotate(360deg); }
|
|
2358
|
+
}
|
|
2198
2359
|
`;
|
|
2199
2360
|
document.head.appendChild(style);
|
|
2200
2361
|
}
|
|
@@ -2211,6 +2372,516 @@ export class WebPlayer extends BasePlayer {
|
|
|
2211
2372
|
const totalWidth = (itemsLength * avgCharWidth) + (config.items.length * gap) + (config.items.length * separatorWidth);
|
|
2212
2373
|
return Math.max(totalWidth / speed, 15);
|
|
2213
2374
|
}
|
|
2375
|
+
createProfessionalTickerElement(config, position) {
|
|
2376
|
+
const broadcastStyle = config.broadcastStyle || {};
|
|
2377
|
+
const theme = broadcastStyle.theme || 'breaking-red';
|
|
2378
|
+
const twoLineConfig = broadcastStyle.twoLineDisplay || {};
|
|
2379
|
+
const itemCycling = config.itemCycling || {};
|
|
2380
|
+
this.tickerConfig = config;
|
|
2381
|
+
this.tickerCurrentItemIndex = 0;
|
|
2382
|
+
const themeColors = this.getBroadcastThemeColors(theme, broadcastStyle);
|
|
2383
|
+
const ticker = document.createElement('div');
|
|
2384
|
+
ticker.className = `uvf-flash-ticker ticker-${position} ticker-professional`;
|
|
2385
|
+
const bottomOffset = config.bottomOffset || 0;
|
|
2386
|
+
const topOffset = config.topOffset || 0;
|
|
2387
|
+
const headerHeight = broadcastStyle.headerHeight || 28;
|
|
2388
|
+
const topLineConfig = twoLineConfig.topLine || {};
|
|
2389
|
+
const bottomLineConfig = twoLineConfig.bottomLine || {};
|
|
2390
|
+
const topLineHeight = topLineConfig.minHeight || 32;
|
|
2391
|
+
const bottomLineHeight = bottomLineConfig.height || 28;
|
|
2392
|
+
const bodyHeight = twoLineConfig.enabled !== false ? topLineHeight + bottomLineHeight : (config.height || 36);
|
|
2393
|
+
const progressHeight = itemCycling.showProgress ? (itemCycling.progressHeight || 3) : 0;
|
|
2394
|
+
const totalHeight = headerHeight + bodyHeight + progressHeight;
|
|
2395
|
+
ticker.style.cssText = `
|
|
2396
|
+
position: absolute;
|
|
2397
|
+
left: 0;
|
|
2398
|
+
right: 0;
|
|
2399
|
+
height: ${totalHeight}px;
|
|
2400
|
+
${position === 'top' ? `top: ${topOffset}px;` : `bottom: ${bottomOffset}px;`}
|
|
2401
|
+
overflow: hidden;
|
|
2402
|
+
pointer-events: ${itemCycling.pauseOnHover !== false ? 'auto' : 'none'};
|
|
2403
|
+
display: flex;
|
|
2404
|
+
flex-direction: column;
|
|
2405
|
+
`;
|
|
2406
|
+
const header = this.createProfessionalHeader(broadcastStyle, themeColors, headerHeight);
|
|
2407
|
+
ticker.appendChild(header);
|
|
2408
|
+
const body = this.createTwoLineBody(config, twoLineConfig, themeColors.bodyBg, bodyHeight);
|
|
2409
|
+
ticker.appendChild(body);
|
|
2410
|
+
if (itemCycling.showProgress) {
|
|
2411
|
+
const progressBar = this.createProgressBar(itemCycling);
|
|
2412
|
+
ticker.appendChild(progressBar);
|
|
2413
|
+
}
|
|
2414
|
+
const introOverlay = this.createIntroOverlay(broadcastStyle);
|
|
2415
|
+
ticker.appendChild(introOverlay);
|
|
2416
|
+
this.tickerIntroOverlay = introOverlay;
|
|
2417
|
+
if (itemCycling.pauseOnHover !== false && itemCycling.enabled) {
|
|
2418
|
+
ticker.addEventListener('mouseenter', () => this.pauseItemCycling());
|
|
2419
|
+
ticker.addEventListener('mouseleave', () => this.resumeItemCycling());
|
|
2420
|
+
}
|
|
2421
|
+
this.ensureTickerAnimations();
|
|
2422
|
+
if (itemCycling.enabled && config.items && config.items.length > 1) {
|
|
2423
|
+
const firstItem = config.items[0];
|
|
2424
|
+
if (firstItem.showIntro) {
|
|
2425
|
+
this.playIntroAnimation(firstItem).then(() => {
|
|
2426
|
+
this.startItemCycling();
|
|
2427
|
+
});
|
|
2428
|
+
}
|
|
2429
|
+
else {
|
|
2430
|
+
this.startItemCycling();
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
return ticker;
|
|
2434
|
+
}
|
|
2435
|
+
createProfessionalHeader(broadcastStyle, themeColors, headerHeight) {
|
|
2436
|
+
const header = document.createElement('div');
|
|
2437
|
+
header.className = 'uvf-ticker-header-row';
|
|
2438
|
+
header.style.cssText = `
|
|
2439
|
+
display: flex;
|
|
2440
|
+
align-items: center;
|
|
2441
|
+
height: ${headerHeight}px;
|
|
2442
|
+
background: ${themeColors.headerBg};
|
|
2443
|
+
padding: 0 12px;
|
|
2444
|
+
position: relative;
|
|
2445
|
+
`;
|
|
2446
|
+
if (broadcastStyle.showGlobe !== false) {
|
|
2447
|
+
const globe = this.createGlobeElement(broadcastStyle.animateGlobe !== false);
|
|
2448
|
+
header.appendChild(globe);
|
|
2449
|
+
}
|
|
2450
|
+
if (broadcastStyle.decorativeShapes?.headerSeparator) {
|
|
2451
|
+
const separator = this.createDecorativeSeparator(broadcastStyle.decorativeShapes.headerSeparator);
|
|
2452
|
+
header.appendChild(separator);
|
|
2453
|
+
}
|
|
2454
|
+
else if (broadcastStyle.showGlobe !== false) {
|
|
2455
|
+
const defaultSeparator = document.createElement('div');
|
|
2456
|
+
defaultSeparator.className = 'uvf-ticker-separator';
|
|
2457
|
+
defaultSeparator.style.cssText = `
|
|
2458
|
+
width: 2px;
|
|
2459
|
+
height: 18px;
|
|
2460
|
+
background: rgba(255,255,255,0.6);
|
|
2461
|
+
margin: 0 10px;
|
|
2462
|
+
`;
|
|
2463
|
+
header.appendChild(defaultSeparator);
|
|
2464
|
+
}
|
|
2465
|
+
const headerText = document.createElement('span');
|
|
2466
|
+
headerText.className = 'uvf-ticker-header-text';
|
|
2467
|
+
headerText.textContent = broadcastStyle.headerText || 'BREAKING NEWS';
|
|
2468
|
+
headerText.style.cssText = `
|
|
2469
|
+
color: ${broadcastStyle.headerTextColor || '#ffffff'};
|
|
2470
|
+
font-size: ${broadcastStyle.headerFontSize || 16}px;
|
|
2471
|
+
font-weight: 800;
|
|
2472
|
+
text-transform: uppercase;
|
|
2473
|
+
letter-spacing: 1px;
|
|
2474
|
+
text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
|
|
2475
|
+
`;
|
|
2476
|
+
header.appendChild(headerText);
|
|
2477
|
+
if (broadcastStyle.showLiveBadge !== false) {
|
|
2478
|
+
const liveBadge = this.createLiveBadgeElement(broadcastStyle.pulseLiveBadge !== false);
|
|
2479
|
+
header.appendChild(liveBadge);
|
|
2480
|
+
}
|
|
2481
|
+
return header;
|
|
2482
|
+
}
|
|
2483
|
+
createDecorativeSeparator(config) {
|
|
2484
|
+
const separator = document.createElement('div');
|
|
2485
|
+
separator.className = 'uvf-ticker-separator';
|
|
2486
|
+
const width = config.width || 2;
|
|
2487
|
+
const height = config.height || 20;
|
|
2488
|
+
const color = config.color || '#ffffff';
|
|
2489
|
+
const animated = config.animated !== false;
|
|
2490
|
+
const type = config.type || 'line';
|
|
2491
|
+
let animationStyle = '';
|
|
2492
|
+
let content = '';
|
|
2493
|
+
switch (type) {
|
|
2494
|
+
case 'pulse-line':
|
|
2495
|
+
animationStyle = animated ? 'animation: separator-pulse 1.5s ease-in-out infinite;' : '';
|
|
2496
|
+
break;
|
|
2497
|
+
case 'chevron':
|
|
2498
|
+
content = '›';
|
|
2499
|
+
animationStyle = animated ? 'animation: chevron-pulse 1s ease-in-out infinite;' : '';
|
|
2500
|
+
break;
|
|
2501
|
+
case 'diamond':
|
|
2502
|
+
content = '◆';
|
|
2503
|
+
animationStyle = animated ? 'animation: diamond-spin 3s linear infinite;' : '';
|
|
2504
|
+
break;
|
|
2505
|
+
case 'dot':
|
|
2506
|
+
content = '•';
|
|
2507
|
+
animationStyle = animated ? 'animation: dot-blink 1s ease-in-out infinite;' : '';
|
|
2508
|
+
break;
|
|
2509
|
+
default:
|
|
2510
|
+
break;
|
|
2511
|
+
}
|
|
2512
|
+
if (type === 'line' || type === 'pulse-line') {
|
|
2513
|
+
separator.style.cssText = `
|
|
2514
|
+
width: ${width}px;
|
|
2515
|
+
height: ${height}px;
|
|
2516
|
+
background: ${color};
|
|
2517
|
+
margin: 0 10px;
|
|
2518
|
+
opacity: 0.8;
|
|
2519
|
+
${animationStyle}
|
|
2520
|
+
`;
|
|
2521
|
+
}
|
|
2522
|
+
else {
|
|
2523
|
+
separator.textContent = content;
|
|
2524
|
+
separator.style.cssText = `
|
|
2525
|
+
color: ${color};
|
|
2526
|
+
font-size: ${height}px;
|
|
2527
|
+
margin: 0 10px;
|
|
2528
|
+
opacity: 0.8;
|
|
2529
|
+
display: flex;
|
|
2530
|
+
align-items: center;
|
|
2531
|
+
justify-content: center;
|
|
2532
|
+
${animationStyle}
|
|
2533
|
+
`;
|
|
2534
|
+
}
|
|
2535
|
+
return separator;
|
|
2536
|
+
}
|
|
2537
|
+
createTwoLineBody(config, twoLineConfig, bodyBg, bodyHeight) {
|
|
2538
|
+
const body = document.createElement('div');
|
|
2539
|
+
body.className = 'uvf-ticker-body-row';
|
|
2540
|
+
body.style.cssText = `
|
|
2541
|
+
display: flex;
|
|
2542
|
+
flex-direction: column;
|
|
2543
|
+
height: ${bodyHeight}px;
|
|
2544
|
+
background: ${bodyBg};
|
|
2545
|
+
overflow: hidden;
|
|
2546
|
+
position: relative;
|
|
2547
|
+
`;
|
|
2548
|
+
const topLineConfig = twoLineConfig.topLine || {};
|
|
2549
|
+
const bottomLineConfig = twoLineConfig.bottomLine || {};
|
|
2550
|
+
const firstItem = config.items?.[0];
|
|
2551
|
+
const headlineLine = document.createElement('div');
|
|
2552
|
+
headlineLine.className = 'uvf-ticker-headline-line';
|
|
2553
|
+
this.tickerHeadlineElement = headlineLine;
|
|
2554
|
+
const topLineHeight = topLineConfig.minHeight || 32;
|
|
2555
|
+
const topLineFontSize = topLineConfig.fontSize || 16;
|
|
2556
|
+
const topLineLineHeight = topLineConfig.lineHeight || 1.3;
|
|
2557
|
+
const topLineMultiLine = topLineConfig.multiLine !== false;
|
|
2558
|
+
const topLineMaxLines = topLineConfig.maxLines || 2;
|
|
2559
|
+
headlineLine.style.cssText = `
|
|
2560
|
+
display: flex;
|
|
2561
|
+
align-items: center;
|
|
2562
|
+
min-height: ${topLineHeight}px;
|
|
2563
|
+
padding: ${topLineConfig.padding || 8}px 12px;
|
|
2564
|
+
background: ${topLineConfig.backgroundColor || 'transparent'};
|
|
2565
|
+
overflow: hidden;
|
|
2566
|
+
${topLineMultiLine ? '' : 'white-space: nowrap;'}
|
|
2567
|
+
`;
|
|
2568
|
+
const headlineText = document.createElement('span');
|
|
2569
|
+
headlineText.className = 'uvf-ticker-headline-text';
|
|
2570
|
+
headlineText.style.cssText = `
|
|
2571
|
+
color: ${topLineConfig.textColor || config.textColor || '#ffffff'};
|
|
2572
|
+
font-size: ${topLineFontSize}px;
|
|
2573
|
+
font-weight: 700;
|
|
2574
|
+
line-height: ${topLineLineHeight};
|
|
2575
|
+
${topLineMultiLine ? `
|
|
2576
|
+
display: -webkit-box;
|
|
2577
|
+
-webkit-line-clamp: ${topLineMaxLines};
|
|
2578
|
+
-webkit-box-orient: vertical;
|
|
2579
|
+
overflow: hidden;
|
|
2580
|
+
text-overflow: ellipsis;
|
|
2581
|
+
white-space: normal;
|
|
2582
|
+
word-wrap: break-word;
|
|
2583
|
+
` : `
|
|
2584
|
+
white-space: nowrap;
|
|
2585
|
+
overflow: hidden;
|
|
2586
|
+
text-overflow: ellipsis;
|
|
2587
|
+
`}
|
|
2588
|
+
`;
|
|
2589
|
+
if (firstItem) {
|
|
2590
|
+
if (firstItem.headlineHtml) {
|
|
2591
|
+
headlineText.innerHTML = firstItem.headlineHtml;
|
|
2592
|
+
}
|
|
2593
|
+
else if (firstItem.headline) {
|
|
2594
|
+
headlineText.textContent = firstItem.headline;
|
|
2595
|
+
}
|
|
2596
|
+
else {
|
|
2597
|
+
headlineText.textContent = firstItem.text;
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
headlineLine.appendChild(headlineText);
|
|
2601
|
+
body.appendChild(headlineLine);
|
|
2602
|
+
if (twoLineConfig.showSeparator !== false) {
|
|
2603
|
+
const separatorLine = document.createElement('div');
|
|
2604
|
+
separatorLine.style.cssText = `
|
|
2605
|
+
height: 1px;
|
|
2606
|
+
background: ${twoLineConfig.separatorColor || 'rgba(255,255,255,0.2)'};
|
|
2607
|
+
margin: 0 12px;
|
|
2608
|
+
`;
|
|
2609
|
+
body.appendChild(separatorLine);
|
|
2610
|
+
}
|
|
2611
|
+
const detailLine = document.createElement('div');
|
|
2612
|
+
detailLine.className = 'uvf-ticker-detail-line';
|
|
2613
|
+
this.tickerDetailElement = detailLine;
|
|
2614
|
+
const bottomLineHeight = bottomLineConfig.height || 28;
|
|
2615
|
+
const bottomLineFontSize = bottomLineConfig.fontSize || 14;
|
|
2616
|
+
const bottomLineSpeed = bottomLineConfig.speed || 80;
|
|
2617
|
+
detailLine.style.cssText = `
|
|
2618
|
+
display: flex;
|
|
2619
|
+
align-items: center;
|
|
2620
|
+
height: ${bottomLineHeight}px;
|
|
2621
|
+
background: ${bottomLineConfig.backgroundColor || 'transparent'};
|
|
2622
|
+
overflow: hidden;
|
|
2623
|
+
position: relative;
|
|
2624
|
+
`;
|
|
2625
|
+
const track = document.createElement('div');
|
|
2626
|
+
track.className = 'uvf-ticker-track';
|
|
2627
|
+
const detailText = firstItem?.text || '';
|
|
2628
|
+
const textWidth = detailText.length * 10;
|
|
2629
|
+
const duration = Math.max(textWidth / bottomLineSpeed, 10);
|
|
2630
|
+
track.style.cssText = `
|
|
2631
|
+
display: flex;
|
|
2632
|
+
white-space: nowrap;
|
|
2633
|
+
animation: ticker-scroll ${duration}s linear infinite;
|
|
2634
|
+
will-change: transform;
|
|
2635
|
+
padding-left: 100%;
|
|
2636
|
+
`;
|
|
2637
|
+
const renderDetail = (text, html) => {
|
|
2638
|
+
for (let i = 0; i < 10; i++) {
|
|
2639
|
+
const span = document.createElement('span');
|
|
2640
|
+
if (html) {
|
|
2641
|
+
span.innerHTML = html;
|
|
2642
|
+
}
|
|
2643
|
+
else {
|
|
2644
|
+
span.textContent = text;
|
|
2645
|
+
}
|
|
2646
|
+
span.style.cssText = `
|
|
2647
|
+
color: ${bottomLineConfig.textColor || config.textColor || '#ffffff'};
|
|
2648
|
+
font-size: ${bottomLineFontSize}px;
|
|
2649
|
+
font-weight: 500;
|
|
2650
|
+
margin-right: 100px;
|
|
2651
|
+
display: inline-flex;
|
|
2652
|
+
align-items: center;
|
|
2653
|
+
`;
|
|
2654
|
+
track.appendChild(span);
|
|
2655
|
+
}
|
|
2656
|
+
};
|
|
2657
|
+
if (firstItem) {
|
|
2658
|
+
renderDetail(firstItem.text, firstItem.html);
|
|
2659
|
+
}
|
|
2660
|
+
detailLine.appendChild(track);
|
|
2661
|
+
body.appendChild(detailLine);
|
|
2662
|
+
return body;
|
|
2663
|
+
}
|
|
2664
|
+
createProgressBar(itemCycling) {
|
|
2665
|
+
const progressBar = document.createElement('div');
|
|
2666
|
+
progressBar.className = 'uvf-ticker-progress';
|
|
2667
|
+
this.tickerProgressBar = progressBar;
|
|
2668
|
+
const height = itemCycling.progressHeight || 3;
|
|
2669
|
+
progressBar.style.cssText = `
|
|
2670
|
+
height: ${height}px;
|
|
2671
|
+
background: rgba(0,0,0,0.3);
|
|
2672
|
+
position: relative;
|
|
2673
|
+
overflow: hidden;
|
|
2674
|
+
`;
|
|
2675
|
+
const progressFill = document.createElement('div');
|
|
2676
|
+
progressFill.className = 'uvf-progress-fill';
|
|
2677
|
+
this.tickerProgressFill = progressFill;
|
|
2678
|
+
progressFill.style.cssText = `
|
|
2679
|
+
height: 100%;
|
|
2680
|
+
width: 0%;
|
|
2681
|
+
background: ${itemCycling.progressColor || '#ffffff'};
|
|
2682
|
+
transition: width 0.1s linear;
|
|
2683
|
+
`;
|
|
2684
|
+
progressBar.appendChild(progressFill);
|
|
2685
|
+
return progressBar;
|
|
2686
|
+
}
|
|
2687
|
+
createIntroOverlay(broadcastStyle) {
|
|
2688
|
+
const overlay = document.createElement('div');
|
|
2689
|
+
overlay.className = 'uvf-ticker-intro-overlay';
|
|
2690
|
+
overlay.style.cssText = `
|
|
2691
|
+
position: absolute;
|
|
2692
|
+
top: 0;
|
|
2693
|
+
left: 0;
|
|
2694
|
+
right: 0;
|
|
2695
|
+
bottom: 0;
|
|
2696
|
+
display: none;
|
|
2697
|
+
align-items: center;
|
|
2698
|
+
justify-content: center;
|
|
2699
|
+
background: rgba(200, 0, 0, 0.95);
|
|
2700
|
+
z-index: 10;
|
|
2701
|
+
`;
|
|
2702
|
+
const introText = document.createElement('span');
|
|
2703
|
+
introText.className = 'uvf-intro-text';
|
|
2704
|
+
introText.style.cssText = `
|
|
2705
|
+
color: #ffffff;
|
|
2706
|
+
font-size: 24px;
|
|
2707
|
+
font-weight: 900;
|
|
2708
|
+
text-transform: uppercase;
|
|
2709
|
+
letter-spacing: 3px;
|
|
2710
|
+
text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
|
|
2711
|
+
`;
|
|
2712
|
+
introText.textContent = 'BREAKING';
|
|
2713
|
+
overlay.appendChild(introText);
|
|
2714
|
+
return overlay;
|
|
2715
|
+
}
|
|
2716
|
+
playIntroAnimation(item) {
|
|
2717
|
+
return new Promise((resolve) => {
|
|
2718
|
+
if (!this.tickerIntroOverlay || !item.showIntro) {
|
|
2719
|
+
resolve();
|
|
2720
|
+
return;
|
|
2721
|
+
}
|
|
2722
|
+
const broadcastStyle = this.tickerConfig?.broadcastStyle || {};
|
|
2723
|
+
const animation = item.introAnimation || broadcastStyle.defaultIntroAnimation || 'slide-in';
|
|
2724
|
+
const duration = item.introDuration || broadcastStyle.defaultIntroDuration || 2000;
|
|
2725
|
+
const introText = item.introText || 'BREAKING';
|
|
2726
|
+
const textElement = this.tickerIntroOverlay.querySelector('.uvf-intro-text');
|
|
2727
|
+
if (textElement) {
|
|
2728
|
+
textElement.textContent = introText;
|
|
2729
|
+
}
|
|
2730
|
+
this.tickerIntroOverlay.style.display = 'flex';
|
|
2731
|
+
this.tickerIntroOverlay.style.animation = `intro-${animation} ${duration}ms ease-out forwards`;
|
|
2732
|
+
setTimeout(() => {
|
|
2733
|
+
if (this.tickerIntroOverlay) {
|
|
2734
|
+
this.tickerIntroOverlay.style.display = 'none';
|
|
2735
|
+
this.tickerIntroOverlay.style.animation = '';
|
|
2736
|
+
}
|
|
2737
|
+
resolve();
|
|
2738
|
+
}, duration);
|
|
2739
|
+
});
|
|
2740
|
+
}
|
|
2741
|
+
startItemCycling() {
|
|
2742
|
+
if (!this.tickerConfig?.items || this.tickerConfig.items.length <= 1)
|
|
2743
|
+
return;
|
|
2744
|
+
const itemCycling = this.tickerConfig.itemCycling || {};
|
|
2745
|
+
if (!itemCycling.enabled)
|
|
2746
|
+
return;
|
|
2747
|
+
const currentItem = this.tickerConfig.items[this.tickerCurrentItemIndex];
|
|
2748
|
+
const duration = (currentItem.duration || itemCycling.defaultDuration || 10) * 1000;
|
|
2749
|
+
this.animateProgress(duration);
|
|
2750
|
+
this.tickerCycleTimer = window.setTimeout(() => {
|
|
2751
|
+
this.transitionToNextItem();
|
|
2752
|
+
}, duration);
|
|
2753
|
+
}
|
|
2754
|
+
stopItemCycling() {
|
|
2755
|
+
if (this.tickerCycleTimer) {
|
|
2756
|
+
clearTimeout(this.tickerCycleTimer);
|
|
2757
|
+
this.tickerCycleTimer = null;
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
pauseItemCycling() {
|
|
2761
|
+
if (!this.tickerCycleTimer || this.tickerIsPaused)
|
|
2762
|
+
return;
|
|
2763
|
+
this.tickerIsPaused = true;
|
|
2764
|
+
this.tickerPauseStartTime = Date.now();
|
|
2765
|
+
const itemCycling = this.tickerConfig?.itemCycling || {};
|
|
2766
|
+
const currentItem = this.tickerConfig?.items?.[this.tickerCurrentItemIndex];
|
|
2767
|
+
const totalDuration = (currentItem?.duration || itemCycling.defaultDuration || 10) * 1000;
|
|
2768
|
+
clearTimeout(this.tickerCycleTimer);
|
|
2769
|
+
this.tickerCycleTimer = null;
|
|
2770
|
+
if (this.tickerProgressFill) {
|
|
2771
|
+
const computedWidth = window.getComputedStyle(this.tickerProgressFill).width;
|
|
2772
|
+
this.tickerProgressFill.style.transition = 'none';
|
|
2773
|
+
this.tickerProgressFill.style.width = computedWidth;
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
resumeItemCycling() {
|
|
2777
|
+
if (!this.tickerIsPaused)
|
|
2778
|
+
return;
|
|
2779
|
+
this.tickerIsPaused = false;
|
|
2780
|
+
const itemCycling = this.tickerConfig?.itemCycling || {};
|
|
2781
|
+
const currentItem = this.tickerConfig?.items?.[this.tickerCurrentItemIndex];
|
|
2782
|
+
const totalDuration = (currentItem?.duration || itemCycling.defaultDuration || 10) * 1000;
|
|
2783
|
+
let remainingTime = totalDuration;
|
|
2784
|
+
if (this.tickerProgressFill) {
|
|
2785
|
+
const currentWidth = parseFloat(this.tickerProgressFill.style.width) || 0;
|
|
2786
|
+
remainingTime = totalDuration * (1 - currentWidth / 100);
|
|
2787
|
+
}
|
|
2788
|
+
this.animateProgress(remainingTime);
|
|
2789
|
+
this.tickerCycleTimer = window.setTimeout(() => {
|
|
2790
|
+
this.transitionToNextItem();
|
|
2791
|
+
}, remainingTime);
|
|
2792
|
+
}
|
|
2793
|
+
animateProgress(duration) {
|
|
2794
|
+
if (!this.tickerProgressFill)
|
|
2795
|
+
return;
|
|
2796
|
+
this.tickerProgressFill.style.transition = 'none';
|
|
2797
|
+
this.tickerProgressFill.style.width = '0%';
|
|
2798
|
+
this.tickerProgressFill.offsetHeight;
|
|
2799
|
+
this.tickerProgressFill.style.transition = `width ${duration}ms linear`;
|
|
2800
|
+
this.tickerProgressFill.style.width = '100%';
|
|
2801
|
+
}
|
|
2802
|
+
async transitionToNextItem() {
|
|
2803
|
+
if (!this.tickerConfig?.items || this.tickerConfig.items.length <= 1)
|
|
2804
|
+
return;
|
|
2805
|
+
const itemCycling = this.tickerConfig.itemCycling || {};
|
|
2806
|
+
const transitionType = itemCycling.transitionType || 'fade';
|
|
2807
|
+
const transitionDuration = itemCycling.transitionDuration || 500;
|
|
2808
|
+
this.tickerCurrentItemIndex = (this.tickerCurrentItemIndex + 1) % this.tickerConfig.items.length;
|
|
2809
|
+
const nextItem = this.tickerConfig.items[this.tickerCurrentItemIndex];
|
|
2810
|
+
if (nextItem.showIntro) {
|
|
2811
|
+
await this.playIntroAnimation(nextItem);
|
|
2812
|
+
}
|
|
2813
|
+
await this.applyItemTransition(transitionType, transitionDuration, nextItem);
|
|
2814
|
+
this.startItemCycling();
|
|
2815
|
+
}
|
|
2816
|
+
applyItemTransition(transitionType, duration, item) {
|
|
2817
|
+
return new Promise((resolve) => {
|
|
2818
|
+
const headline = this.tickerHeadlineElement?.querySelector('.uvf-ticker-headline-text');
|
|
2819
|
+
const detailTrack = this.tickerDetailElement?.querySelector('.uvf-ticker-track');
|
|
2820
|
+
if (!headline || !detailTrack) {
|
|
2821
|
+
this.updateItemContent(item);
|
|
2822
|
+
resolve();
|
|
2823
|
+
return;
|
|
2824
|
+
}
|
|
2825
|
+
if (transitionType === 'none') {
|
|
2826
|
+
this.updateItemContent(item);
|
|
2827
|
+
resolve();
|
|
2828
|
+
return;
|
|
2829
|
+
}
|
|
2830
|
+
const outAnimation = transitionType === 'slide' ? 'item-slide-out' : 'item-fade-out';
|
|
2831
|
+
const inAnimation = transitionType === 'slide' ? 'item-slide-in' : 'item-fade-in';
|
|
2832
|
+
headline.style.animation = `${outAnimation} ${duration / 2}ms ease-out forwards`;
|
|
2833
|
+
setTimeout(() => {
|
|
2834
|
+
this.updateItemContent(item);
|
|
2835
|
+
headline.style.animation = `${inAnimation} ${duration / 2}ms ease-out forwards`;
|
|
2836
|
+
setTimeout(() => {
|
|
2837
|
+
headline.style.animation = '';
|
|
2838
|
+
resolve();
|
|
2839
|
+
}, duration / 2);
|
|
2840
|
+
}, duration / 2);
|
|
2841
|
+
});
|
|
2842
|
+
}
|
|
2843
|
+
updateItemContent(item) {
|
|
2844
|
+
const headline = this.tickerHeadlineElement?.querySelector('.uvf-ticker-headline-text');
|
|
2845
|
+
if (headline) {
|
|
2846
|
+
if (item.headlineHtml) {
|
|
2847
|
+
headline.innerHTML = item.headlineHtml;
|
|
2848
|
+
}
|
|
2849
|
+
else if (item.headline) {
|
|
2850
|
+
headline.textContent = item.headline;
|
|
2851
|
+
}
|
|
2852
|
+
else {
|
|
2853
|
+
headline.textContent = item.text;
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
const detailTrack = this.tickerDetailElement?.querySelector('.uvf-ticker-track');
|
|
2857
|
+
if (detailTrack) {
|
|
2858
|
+
detailTrack.innerHTML = '';
|
|
2859
|
+
const bottomLineConfig = this.tickerConfig?.broadcastStyle?.twoLineDisplay?.bottomLine || {};
|
|
2860
|
+
const bottomLineFontSize = bottomLineConfig.fontSize || 14;
|
|
2861
|
+
for (let i = 0; i < 10; i++) {
|
|
2862
|
+
const span = document.createElement('span');
|
|
2863
|
+
if (item.html) {
|
|
2864
|
+
span.innerHTML = item.html;
|
|
2865
|
+
}
|
|
2866
|
+
else {
|
|
2867
|
+
span.textContent = item.text;
|
|
2868
|
+
}
|
|
2869
|
+
span.style.cssText = `
|
|
2870
|
+
color: ${bottomLineConfig.textColor || this.tickerConfig?.textColor || '#ffffff'};
|
|
2871
|
+
font-size: ${bottomLineFontSize}px;
|
|
2872
|
+
font-weight: 500;
|
|
2873
|
+
margin-right: 100px;
|
|
2874
|
+
display: inline-flex;
|
|
2875
|
+
align-items: center;
|
|
2876
|
+
`;
|
|
2877
|
+
detailTrack.appendChild(span);
|
|
2878
|
+
}
|
|
2879
|
+
const bottomLineSpeed = bottomLineConfig.speed || 80;
|
|
2880
|
+
const textWidth = item.text.length * 10;
|
|
2881
|
+
const duration = Math.max(textWidth / bottomLineSpeed, 10);
|
|
2882
|
+
detailTrack.style.animation = `ticker-scroll ${duration}s linear infinite`;
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2214
2885
|
setAutoQuality(enabled) {
|
|
2215
2886
|
this.autoQuality = enabled;
|
|
2216
2887
|
if (this.hls) {
|