UNPKG

@ngx-telly/player

Version:

Telly - Angular frame-accurate HTML player

530 lines (518 loc) 77.5 kB
import * as i0 from '@angular/core'; import { input, output, inject, ElementRef, HostListener, HostBinding, ViewChild, ChangeDetectionStrategy, Component, ChangeDetectorRef, DOCUMENT, Input, effect, Pipe } from '@angular/core'; import { tellyConfigToken, debounceMove, TellyMedia, BROWSER, VideoEvent, eventType, Browser, fastForward, fullScreen, smallScreen, play, pause, rewind, getTime, mute, volumeLow, volumeMedium, volumeHigh, fwd60, fwd45, fwd30, fwd15, fwd10, fwd5, rwd60, rwd45, rwd30, rwd15, rwd10, rwd5 } from '@ngx-telly/player/core'; import { TellyMediaService, TellyEventsService, TellyDurationCapperService, TellyPluginOrchestratorService, TellyFullscreenService } from '@ngx-telly/player/services'; import { tap, timer, merge, fromEvent, takeUntil, Subject, debounceTime, map, startWith, distinctUntilChanged, BehaviorSubject } from 'rxjs'; import { AsyncPipe } from '@angular/common'; class PlayerComponent { constructor() { this.id = input.required(...(ngDevMode ? [{ debugName: "id" }] : [])); this.src = input(...(ngDevMode ? [undefined, { debugName: "src" }] : [])); this.poster = input(...(ngDevMode ? [undefined, { debugName: "poster" }] : [])); this.controlsList = input(...(ngDevMode ? [undefined, { debugName: "controlsList" }] : [])); this.showId = input(false, ...(ngDevMode ? [{ debugName: "showId" }] : [])); this.autoHide = input(true, ...(ngDevMode ? [{ debugName: "autoHide" }] : [])); this.autoPlay = input(false, ...(ngDevMode ? [{ debugName: "autoPlay" }] : [])); this.autoPip = input(false, ...(ngDevMode ? [{ debugName: "autoPip" }] : [])); this.disablePip = input(false, ...(ngDevMode ? [{ debugName: "disablePip" }] : [])); this.controls = input(true, ...(ngDevMode ? [{ debugName: "controls" }] : [])); this.loop = input(false, ...(ngDevMode ? [{ debugName: "loop" }] : [])); this.muted = input(false, ...(ngDevMode ? [{ debugName: "muted" }] : [])); this.disableRemotePlayback = input(false, ...(ngDevMode ? [{ debugName: "disableRemotePlayback" }] : [])); this.height = input(...(ngDevMode ? [undefined, { debugName: "height" }] : [])); this.width = input(...(ngDevMode ? [undefined, { debugName: "width" }] : [])); this.capToDuration = input(0, ...(ngDevMode ? [{ debugName: "capToDuration" }] : [])); this.updateInterval = input(1000, ...(ngDevMode ? [{ debugName: "updateInterval" }] : [])); this.hideDelay = input(3000, ...(ngDevMode ? [{ debugName: "hideDelay" }] : [])); this.startFrom = input(0, ...(ngDevMode ? [{ debugName: "startFrom" }] : [])); this.timeOffset = input(0, ...(ngDevMode ? [{ debugName: "timeOffset" }] : [])); this.crossOrigin = input(...(ngDevMode ? [undefined, { debugName: "crossOrigin" }] : [])); this.preload = input(...(ngDevMode ? [undefined, { debugName: "preload" }] : [])); this.clickable = input(true, ...(ngDevMode ? [{ debugName: "clickable" }] : [])); this.created = output(); this.hide = false; this.config = inject(tellyConfigToken); this.element = inject(ElementRef).nativeElement; this.mediaService = inject(TellyMediaService); this.events = inject(TellyEventsService); this.capper = inject(TellyDurationCapperService); this.orchestrator = inject(TellyPluginOrchestratorService); } onClick() { this.events.click$.next(); } ngOnInit() { this.setTimer(); this.setEvents(); this.events.activity$ .pipe(debounceMove(3000, this.events.destroy$, (hide) => (this.hide = hide)), tap((hide) => this.events.hide$.next(hide))) .subscribe(); this.orchestrator.registerMediaFactory(); this.media = this.create(); this.created.emit(this.media); if (this.capToDuration()) this.capper.set(this.capToDuration(), this.media); } ngOnChanges(changes) { if (changes['capToDuration']?.currentValue && !changes['capToDuration'].firstChange) { this.events.reconfigure$.next(); this.capper.set(changes['capToDuration'].currentValue, this.media); } } onContainerClick() { if (this.clickable()) { this.media.toggle(); } } create() { const media = new TellyMedia(this.id(), this.video.nativeElement, this.config); this.mediaService.register(media); return this.mediaService.get(this.id()); } setTimer() { this.events.timer$ = timer(0, this.updateInterval()); } setEvents() { this.events.activity$ = merge(fromEvent(this.element, 'mousemove'), fromEvent(this.element, 'touchmove'), fromEvent(this.element, 'touchend'), fromEvent(this.element, 'scroll'), fromEvent(this.element, 'wheel'), fromEvent(this.element, 'click')); fromEvent(this.video.nativeElement, 'click') .pipe(takeUntil(this.events.destroy$)) .subscribe(() => this.events.click$.next); } ngOnDestroy() { this.events.destroy$.next(); if (this.media?.id) this.mediaService.deregister(this.media.id); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: PlayerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.18", type: PlayerComponent, isStandalone: true, selector: "telly-player", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, poster: { classPropertyName: "poster", publicName: "poster", isSignal: true, isRequired: false, transformFunction: null }, controlsList: { classPropertyName: "controlsList", publicName: "controlsList", isSignal: true, isRequired: false, transformFunction: null }, showId: { classPropertyName: "showId", publicName: "showId", isSignal: true, isRequired: false, transformFunction: null }, autoHide: { classPropertyName: "autoHide", publicName: "autoHide", isSignal: true, isRequired: false, transformFunction: null }, autoPlay: { classPropertyName: "autoPlay", publicName: "autoPlay", isSignal: true, isRequired: false, transformFunction: null }, autoPip: { classPropertyName: "autoPip", publicName: "autoPip", isSignal: true, isRequired: false, transformFunction: null }, disablePip: { classPropertyName: "disablePip", publicName: "disablePip", isSignal: true, isRequired: false, transformFunction: null }, controls: { classPropertyName: "controls", publicName: "controls", isSignal: true, isRequired: false, transformFunction: null }, loop: { classPropertyName: "loop", publicName: "loop", isSignal: true, isRequired: false, transformFunction: null }, muted: { classPropertyName: "muted", publicName: "muted", isSignal: true, isRequired: false, transformFunction: null }, disableRemotePlayback: { classPropertyName: "disableRemotePlayback", publicName: "disableRemotePlayback", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, capToDuration: { classPropertyName: "capToDuration", publicName: "capToDuration", isSignal: true, isRequired: false, transformFunction: null }, updateInterval: { classPropertyName: "updateInterval", publicName: "updateInterval", isSignal: true, isRequired: false, transformFunction: null }, hideDelay: { classPropertyName: "hideDelay", publicName: "hideDelay", isSignal: true, isRequired: false, transformFunction: null }, startFrom: { classPropertyName: "startFrom", publicName: "startFrom", isSignal: true, isRequired: false, transformFunction: null }, timeOffset: { classPropertyName: "timeOffset", publicName: "timeOffset", isSignal: true, isRequired: false, transformFunction: null }, crossOrigin: { classPropertyName: "crossOrigin", publicName: "crossOrigin", isSignal: true, isRequired: false, transformFunction: null }, preload: { classPropertyName: "preload", publicName: "preload", isSignal: true, isRequired: false, transformFunction: null }, clickable: { classPropertyName: "clickable", publicName: "clickable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { created: "created" }, host: { listeners: { "pointerup": "onClick()" }, properties: { "class.hide": "this.hide" } }, providers: [TellyDurationCapperService], viewQueries: [{ propertyName: "video", first: true, predicate: ["video"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"telly-container\" (click)=\"onContainerClick()\">\n <div class=\"telly-overlay\">\n <video\n #video\n playsinline\n [src]=\"src()\"\n [autoplay]=\"autoPlay()\"\n [controls]=\"controls()\"\n [loop]=\"loop()\"\n [muted]=\"muted()\"\n [disableRemotePlayback]=\"disableRemotePlayback()\"\n [crossOrigin]=\"crossOrigin()\"\n [attr.preload]=\"preload()\"\n [attr.poster]=\"poster()\"\n [attr.height]=\"height()\"\n [attr.width]=\"width()\"\n >\n </video>\n <ng-content select=\"[overlay]\"></ng-content>\n </div>\n\n @if (media) {\n <ng-content></ng-content>\n }\n</div>\n\n<svg style=\"position: absolute\">\n <linearGradient\n id=\"telly-gradient\"\n gradientTransform=\"rotate(65)\"\n >\n <stop\n offset=\"0%\"\n stop-color=\"#F24C13\"\n />\n <stop\n offset=\"50%\"\n stop-color=\"#f36c15\"\n />\n <stop\n offset=\"100%\"\n stop-color=\"#F48616\"\n />\n </linearGradient>\n</svg>\n", styles: [":host{position:relative;overflow:hidden;display:flex;justify-content:center;align-items:center}:host(.hide){cursor:none}.telly-container{position:relative;overflow:hidden;display:flex;justify-content:center;align-items:center;width:100%;z-index:90}.telly-overlay{width:100%;height:100%;position:relative;display:flex;justify-content:center;align-items:center}video{background:#000;width:100%;vertical-align:baseline;margin-left:auto;margin-right:auto}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: PlayerComponent, decorators: [{ type: Component, args: [{ selector: 'telly-player', providers: [TellyDurationCapperService], standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"telly-container\" (click)=\"onContainerClick()\">\n <div class=\"telly-overlay\">\n <video\n #video\n playsinline\n [src]=\"src()\"\n [autoplay]=\"autoPlay()\"\n [controls]=\"controls()\"\n [loop]=\"loop()\"\n [muted]=\"muted()\"\n [disableRemotePlayback]=\"disableRemotePlayback()\"\n [crossOrigin]=\"crossOrigin()\"\n [attr.preload]=\"preload()\"\n [attr.poster]=\"poster()\"\n [attr.height]=\"height()\"\n [attr.width]=\"width()\"\n >\n </video>\n <ng-content select=\"[overlay]\"></ng-content>\n </div>\n\n @if (media) {\n <ng-content></ng-content>\n }\n</div>\n\n<svg style=\"position: absolute\">\n <linearGradient\n id=\"telly-gradient\"\n gradientTransform=\"rotate(65)\"\n >\n <stop\n offset=\"0%\"\n stop-color=\"#F24C13\"\n />\n <stop\n offset=\"50%\"\n stop-color=\"#f36c15\"\n />\n <stop\n offset=\"100%\"\n stop-color=\"#F48616\"\n />\n </linearGradient>\n</svg>\n", styles: [":host{position:relative;overflow:hidden;display:flex;justify-content:center;align-items:center}:host(.hide){cursor:none}.telly-container{position:relative;overflow:hidden;display:flex;justify-content:center;align-items:center;width:100%;z-index:90}.telly-overlay{width:100%;height:100%;position:relative;display:flex;justify-content:center;align-items:center}video{background:#000;width:100%;vertical-align:baseline;margin-left:auto;margin-right:auto}\n"] }] }], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }], src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], poster: [{ type: i0.Input, args: [{ isSignal: true, alias: "poster", required: false }] }], controlsList: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlsList", required: false }] }], showId: [{ type: i0.Input, args: [{ isSignal: true, alias: "showId", required: false }] }], autoHide: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoHide", required: false }] }], autoPlay: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoPlay", required: false }] }], autoPip: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoPip", required: false }] }], disablePip: [{ type: i0.Input, args: [{ isSignal: true, alias: "disablePip", required: false }] }], controls: [{ type: i0.Input, args: [{ isSignal: true, alias: "controls", required: false }] }], loop: [{ type: i0.Input, args: [{ isSignal: true, alias: "loop", required: false }] }], muted: [{ type: i0.Input, args: [{ isSignal: true, alias: "muted", required: false }] }], disableRemotePlayback: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableRemotePlayback", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], capToDuration: [{ type: i0.Input, args: [{ isSignal: true, alias: "capToDuration", required: false }] }], updateInterval: [{ type: i0.Input, args: [{ isSignal: true, alias: "updateInterval", required: false }] }], hideDelay: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideDelay", required: false }] }], startFrom: [{ type: i0.Input, args: [{ isSignal: true, alias: "startFrom", required: false }] }], timeOffset: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeOffset", required: false }] }], crossOrigin: [{ type: i0.Input, args: [{ isSignal: true, alias: "crossOrigin", required: false }] }], preload: [{ type: i0.Input, args: [{ isSignal: true, alias: "preload", required: false }] }], clickable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clickable", required: false }] }], created: [{ type: i0.Output, args: ["created"] }], video: [{ type: ViewChild, args: ['video', { static: true }] }], hide: [{ type: HostBinding, args: ['class.hide'] }], onClick: [{ type: HostListener, args: ['pointerup'] }] } }); class BufferingComponent { constructor() { this.player = inject(PlayerComponent); this.events = inject(TellyEventsService); this.platform = inject(BROWSER); this.play = false; this.suspend$ = new Subject(); this.hide = false; this.chromiumWatcher = (ev) => { if (ev.type === VideoEvent.Playing) { return false; } if (ev.type === VideoEvent.Waiting) { return true; } if (ev.type === VideoEvent.Ended) { return false; } if (ev.type === VideoEvent.Suspend) { return false; } return; }; this.webkitWatcher = (ev) => { return this.chromiumWatcher(ev); }; this.firefoxWatcher = (ev) => { return this.chromiumWatcher(ev); }; } ngOnInit() { const watcher = this.getWatcher(); merge(this.player.media?.events, this.suspend$.pipe(debounceTime(4000))) .pipe(eventType([ VideoEvent.Playing, VideoEvent.Pause, VideoEvent.Progress, VideoEvent.Stalled, VideoEvent.Suspend, VideoEvent.Seeked, VideoEvent.Seeking, VideoEvent.Waiting ]), tap((ev) => { switch (ev.type) { case VideoEvent.Playing: this.play = true; break; case VideoEvent.Pause: this.play = false; break; } }), tap((ev) => { if (ev.type === VideoEvent.Progress) { this.suspend$.next(new Event(VideoEvent.Suspend)); } }), map((ev) => { return watcher(ev); })) .subscribe((buffering) => { if (typeof buffering !== 'boolean') return; this.hide = !buffering; }); } getWatcher() { switch (this.platform) { case Browser.Chromium: return this.chromiumWatcher; case Browser.Webkit: return this.webkitWatcher; case Browser.Firefox: return this.firefoxWatcher; case Browser.Ios: return this.webkitWatcher; default: return this.chromiumWatcher; } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: BufferingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: BufferingComponent, isStandalone: true, selector: "telly-buffering", host: { properties: { "class.hide": "this.hide" } }, ngImport: i0, template: "<svg\n width=\"135\"\n height=\"140\"\n viewBox=\"0 0 135 140\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"#fff\"\n>\n <rect\n y=\"10\"\n width=\"15\"\n height=\"120\"\n rx=\"6\"\n >\n <animate\n attributeName=\"height\"\n begin=\"0.5s\"\n dur=\"1s\"\n values=\"120;110;100;90;80;70;60;50;40;140;120\"\n calcMode=\"linear\"\n repeatCount=\"indefinite\"\n />\n <animate\n attributeName=\"y\"\n begin=\"0.5s\"\n dur=\"1s\"\n values=\"10;15;20;25;30;35;40;45;50;0;10\"\n calcMode=\"linear\"\n repeatCount=\"indefinite\"\n />\n </rect>\n <rect\n x=\"30\"\n y=\"10\"\n width=\"15\"\n height=\"120\"\n rx=\"6\"\n >\n <animate\n attributeName=\"height\"\n begin=\"0.25s\"\n dur=\"1s\"\n values=\"120;110;100;90;80;70;60;50;40;140;120\"\n calcMode=\"linear\"\n repeatCount=\"indefinite\"\n />\n <animate\n attributeName=\"y\"\n begin=\"0.25s\"\n dur=\"1s\"\n values=\"10;15;20;25;30;35;40;45;50;0;10\"\n calcMode=\"linear\"\n repeatCount=\"indefinite\"\n />\n </rect>\n <rect\n x=\"60\"\n width=\"15\"\n height=\"140\"\n rx=\"6\"\n >\n <animate\n attributeName=\"height\"\n begin=\"0s\"\n dur=\"1s\"\n values=\"120;110;100;90;80;70;60;50;40;140;120\"\n calcMode=\"linear\"\n repeatCount=\"indefinite\"\n />\n <animate\n attributeName=\"y\"\n begin=\"0s\"\n dur=\"1s\"\n values=\"10;15;20;25;30;35;40;45;50;0;10\"\n calcMode=\"linear\"\n repeatCount=\"indefinite\"\n />\n </rect>\n <rect\n x=\"90\"\n y=\"10\"\n width=\"15\"\n height=\"120\"\n rx=\"6\"\n >\n <animate\n attributeName=\"height\"\n begin=\"0.25s\"\n dur=\"1s\"\n values=\"120;110;100;90;80;70;60;50;40;140;120\"\n calcMode=\"linear\"\n repeatCount=\"indefinite\"\n />\n <animate\n attributeName=\"y\"\n begin=\"0.25s\"\n dur=\"1s\"\n values=\"10;15;20;25;30;35;40;45;50;0;10\"\n calcMode=\"linear\"\n repeatCount=\"indefinite\"\n />\n </rect>\n <rect\n x=\"120\"\n y=\"10\"\n width=\"15\"\n height=\"120\"\n rx=\"6\"\n >\n <animate\n attributeName=\"height\"\n begin=\"0.5s\"\n dur=\"1s\"\n values=\"120;110;100;90;80;70;60;50;40;140;120\"\n calcMode=\"linear\"\n repeatCount=\"indefinite\"\n />\n <animate\n attributeName=\"y\"\n begin=\"0.5s\"\n dur=\"1s\"\n values=\"10;15;20;25;30;35;40;45;50;0;10\"\n calcMode=\"linear\"\n repeatCount=\"indefinite\"\n />\n </rect>\n</svg>\n", styles: [":host{height:100px;width:100px;opacity:100;display:flex;justify-content:center;align-items:center;padding:2rem 1.5rem;border-radius:12px;border:1px solid rgba(54,54,54,.05);box-shadow:0 5px 15px #00000080;box-sizing:border-box;-webkit-backdrop-filter:blur(20px);backdrop-filter:blur(20px);background-blend-mode:overlay;position:absolute;background-color:#50505080;color:#fffc;transition:opacity .2s linear,bottom .4s ease-in-out}:host(.hide){opacity:0}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: BufferingComponent, decorators: [{ type: Component, args: [{ selector: 'telly-buffering', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<svg\n width=\"135\"\n height=\"140\"\n viewBox=\"0 0 135 140\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"#fff\"\n>\n <rect\n y=\"10\"\n width=\"15\"\n height=\"120\"\n rx=\"6\"\n >\n <animate\n attributeName=\"height\"\n begin=\"0.5s\"\n dur=\"1s\"\n values=\"120;110;100;90;80;70;60;50;40;140;120\"\n calcMode=\"linear\"\n repeatCount=\"indefinite\"\n />\n <animate\n attributeName=\"y\"\n begin=\"0.5s\"\n dur=\"1s\"\n values=\"10;15;20;25;30;35;40;45;50;0;10\"\n calcMode=\"linear\"\n repeatCount=\"indefinite\"\n />\n </rect>\n <rect\n x=\"30\"\n y=\"10\"\n width=\"15\"\n height=\"120\"\n rx=\"6\"\n >\n <animate\n attributeName=\"height\"\n begin=\"0.25s\"\n dur=\"1s\"\n values=\"120;110;100;90;80;70;60;50;40;140;120\"\n calcMode=\"linear\"\n repeatCount=\"indefinite\"\n />\n <animate\n attributeName=\"y\"\n begin=\"0.25s\"\n dur=\"1s\"\n values=\"10;15;20;25;30;35;40;45;50;0;10\"\n calcMode=\"linear\"\n repeatCount=\"indefinite\"\n />\n </rect>\n <rect\n x=\"60\"\n width=\"15\"\n height=\"140\"\n rx=\"6\"\n >\n <animate\n attributeName=\"height\"\n begin=\"0s\"\n dur=\"1s\"\n values=\"120;110;100;90;80;70;60;50;40;140;120\"\n calcMode=\"linear\"\n repeatCount=\"indefinite\"\n />\n <animate\n attributeName=\"y\"\n begin=\"0s\"\n dur=\"1s\"\n values=\"10;15;20;25;30;35;40;45;50;0;10\"\n calcMode=\"linear\"\n repeatCount=\"indefinite\"\n />\n </rect>\n <rect\n x=\"90\"\n y=\"10\"\n width=\"15\"\n height=\"120\"\n rx=\"6\"\n >\n <animate\n attributeName=\"height\"\n begin=\"0.25s\"\n dur=\"1s\"\n values=\"120;110;100;90;80;70;60;50;40;140;120\"\n calcMode=\"linear\"\n repeatCount=\"indefinite\"\n />\n <animate\n attributeName=\"y\"\n begin=\"0.25s\"\n dur=\"1s\"\n values=\"10;15;20;25;30;35;40;45;50;0;10\"\n calcMode=\"linear\"\n repeatCount=\"indefinite\"\n />\n </rect>\n <rect\n x=\"120\"\n y=\"10\"\n width=\"15\"\n height=\"120\"\n rx=\"6\"\n >\n <animate\n attributeName=\"height\"\n begin=\"0.5s\"\n dur=\"1s\"\n values=\"120;110;100;90;80;70;60;50;40;140;120\"\n calcMode=\"linear\"\n repeatCount=\"indefinite\"\n />\n <animate\n attributeName=\"y\"\n begin=\"0.5s\"\n dur=\"1s\"\n values=\"10;15;20;25;30;35;40;45;50;0;10\"\n calcMode=\"linear\"\n repeatCount=\"indefinite\"\n />\n </rect>\n</svg>\n", styles: [":host{height:100px;width:100px;opacity:100;display:flex;justify-content:center;align-items:center;padding:2rem 1.5rem;border-radius:12px;border:1px solid rgba(54,54,54,.05);box-shadow:0 5px 15px #00000080;box-sizing:border-box;-webkit-backdrop-filter:blur(20px);backdrop-filter:blur(20px);background-blend-mode:overlay;position:absolute;background-color:#50505080;color:#fffc;transition:opacity .2s linear,bottom .4s ease-in-out}:host(.hide){opacity:0}\n"] }] }], propDecorators: { hide: [{ type: HostBinding, args: ['class.hide'] }] } }); class MessageOutletComponent { constructor() { this.player = inject(PlayerComponent); this.events = inject(TellyEventsService); this.cdr = inject(ChangeDetectorRef); this.show = false; } ngOnInit() { this.player.media?.messages .pipe(takeUntil(this.events.destroy$), tap(() => { this.show = true; this.cdr.markForCheck(); }), debounceTime(1500)) .subscribe(() => { this.show = false; this.cdr.markForCheck(); }); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: MessageOutletComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: MessageOutletComponent, isStandalone: true, selector: "telly-message-outlet", ngImport: i0, template: "<span class=\"indicator\" [style.opacity]=\"show ? 1 : 0\">{{ player.media.internalEvents$ | async }}</span>\n", styles: [":host{color:var(--telly-text);position:absolute;right:1rem;top:1rem}.indicator{margin:1rem;font-size:1.5rem;line-height:3rem;font-family:monospace;text-transform:uppercase;text-shadow:4px 4px black;transition:all .2s ease-in-out}\n"], dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: MessageOutletComponent, decorators: [{ type: Component, args: [{ selector: 'telly-message-outlet', changeDetection: ChangeDetectionStrategy.OnPush, imports: [AsyncPipe], template: "<span class=\"indicator\" [style.opacity]=\"show ? 1 : 0\">{{ player.media.internalEvents$ | async }}</span>\n", styles: [":host{color:var(--telly-text);position:absolute;right:1rem;top:1rem}.indicator{margin:1rem;font-size:1.5rem;line-height:3rem;font-family:monospace;text-transform:uppercase;text-shadow:4px 4px black;transition:all .2s ease-in-out}\n"] }] }] }); class ControlsComponent { constructor() { this.class = 'telly-element'; this.hide = false; this.events = inject(TellyEventsService); this.player = inject(PlayerComponent); this.doc = inject(DOCUMENT); } onClick(event) { event.stopPropagation(); } ngOnInit() { this.events.activity$ .pipe(debounceMove(this.player.hideDelay(), this.events.destroy$, (hide) => { if (this.doc.fullscreenElement || this.player.autoHide()) { this.hide = hide; } })) .subscribe(); fromEvent(this.player.element, 'fullscreenchange') .pipe(takeUntil(this.events.destroy$)) .subscribe(() => { const fullscreen = !!this.doc.fullscreenElement; if (!fullscreen) { this.hide = false; } }); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ControlsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: ControlsComponent, isStandalone: true, selector: "telly-controls", inputs: { class: "class" }, host: { listeners: { "click": "onClick($event)" }, properties: { "class": "this.class", "class.hide": "this.hide" } }, ngImport: i0, template: "<ng-content></ng-content>\n", styles: [":host{width:95%;opacity:100;bottom:0;height:50px;box-sizing:border-box;font-family:monospace;display:flex;align-items:center;align-self:flex-end;justify-content:space-between;position:absolute;margin-bottom:1rem;transition:opacity .2s linear,bottom .4s ease-in-out;padding:1rem;z-index:100}:host(.hide){opacity:0;bottom:-10%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ControlsComponent, decorators: [{ type: Component, args: [{ selector: 'telly-controls', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>\n", styles: [":host{width:95%;opacity:100;bottom:0;height:50px;box-sizing:border-box;font-family:monospace;display:flex;align-items:center;align-self:flex-end;justify-content:space-between;position:absolute;margin-bottom:1rem;transition:opacity .2s linear,bottom .4s ease-in-out;padding:1rem;z-index:100}:host(.hide){opacity:0;bottom:-10%}\n"] }] }], propDecorators: { class: [{ type: Input }, { type: HostBinding, args: ['class'] }], hide: [{ type: HostBinding, args: ['class.hide'] }], onClick: [{ type: HostListener, args: ['click', ['$event']] }] } }); class ForwardComponent { constructor() { this.icon = input(fastForward, ...(ngDevMode ? [{ debugName: "icon" }] : [])); this.size = input('1.6rem', ...(ngDevMode ? [{ debugName: "size" }] : [])); this.player = inject(PlayerComponent); this.playing = false; this.handle = false; this.title = 'Forward'; } onPointerDown() { this.playing = this.player.media.playing; this.handle = true; this.player.media.setRate(4); } onPointerUpOrLeave() { if (this.handle) { this.player.media.setRate(1); this.playing ? this.player.media.play() : this.player.media.pause(); this.handle = false; } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ForwardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.18", type: ForwardComponent, isStandalone: true, selector: "telly-forward", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "pointerdown": "onPointerDown()", "pointerup": "onPointerUpOrLeave()", "pointerleave": "onPointerUpOrLeave()" }, properties: { "attr.tip": "this.title" } }, ngImport: i0, template: "<svg\n viewBox=\"0 0 24 24\"\n [style.width]=\"size()\"\n>\n <path\n fill=\"white\"\n [attr.d]=\"icon()\"\n />\n</svg>\n", styles: [""], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ForwardComponent, decorators: [{ type: Component, args: [{ selector: 'telly-forward', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<svg\n viewBox=\"0 0 24 24\"\n [style.width]=\"size()\"\n>\n <path\n fill=\"white\"\n [attr.d]=\"icon()\"\n />\n</svg>\n" }] }], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], title: [{ type: HostBinding, args: ['attr.tip'] }], onPointerDown: [{ type: HostListener, args: ['pointerdown'] }], onPointerUpOrLeave: [{ type: HostListener, args: ['pointerup'] }, { type: HostListener, args: ['pointerleave'] }] } }); class FullscreenComponent { constructor() { this.icon = input(fullScreen, ...(ngDevMode ? [{ debugName: "icon" }] : [])); this.alternativeIcon = input(smallScreen, ...(ngDevMode ? [{ debugName: "alternativeIcon" }] : [])); this.size = input('1.6rem', ...(ngDevMode ? [{ debugName: "size" }] : [])); this.player = inject(PlayerComponent); this.events = inject(TellyEventsService); this.cdr = inject(ChangeDetectorRef); this.doc = inject(DOCUMENT); this.service = inject(TellyFullscreenService); this.title = 'Big'; effect(() => { if (this.service.fullscreen()) { this.player.element.requestFullscreen(); } else { this.doc.fullscreenElement != null && this.doc?.exitFullscreen(); } }); } ngOnInit() { fromEvent(this.player.element, 'fullscreenchange') .pipe(takeUntil(this.events.destroy$)) .subscribe(() => { this.service.fullscreen.set(!!this.doc.fullscreenElement); this.title = this.service.fullscreen() ? 'Small' : 'Big'; this.cdr.markForCheck(); }); } onClick() { this.service.toggle(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: FullscreenComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.18", type: FullscreenComponent, isStandalone: true, selector: "telly-fullscreen", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, alternativeIcon: { classPropertyName: "alternativeIcon", publicName: "alternativeIcon", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "onClick()" }, properties: { "attr.tip": "this.title" } }, ngImport: i0, template: "<svg\n viewBox=\"0 0 24 24\"\n [style.width]=\"size()\"\n>\n <path\n fill=\"white\"\n [attr.d]=\"service.fullscreen() ? alternativeIcon() : icon()\"\n />\n</svg>\n", styles: [""], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: FullscreenComponent, decorators: [{ type: Component, args: [{ selector: 'telly-fullscreen', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<svg\n viewBox=\"0 0 24 24\"\n [style.width]=\"size()\"\n>\n <path\n fill=\"white\"\n [attr.d]=\"service.fullscreen() ? alternativeIcon() : icon()\"\n />\n</svg>\n" }] }], ctorParameters: () => [], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], alternativeIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "alternativeIcon", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], title: [{ type: HostBinding, args: ['attr.tip'] }], onClick: [{ type: HostListener, args: ['click'] }] } }); class PlayComponent { constructor() { this.playIcon = input(play, ...(ngDevMode ? [{ debugName: "playIcon" }] : [])); this.pauseIcon = input(pause, ...(ngDevMode ? [{ debugName: "pauseIcon" }] : [])); this.size = input('1.2rem', ...(ngDevMode ? [{ debugName: "size" }] : [])); this.player = inject(PlayerComponent); this.title = 'Toggle'; } onClick() { this.player.media?.toggle(); } ngOnInit() { this.status$ = this.player.media.events.pipe(startWith(new Event(VideoEvent.Pause)), eventType([VideoEvent.Play, VideoEvent.Pause, VideoEvent.Ended]), map(({ type }) => (type === VideoEvent.Play ? this.pauseIcon() : this.playIcon()))); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: PlayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.18", type: PlayComponent, isStandalone: true, selector: "telly-play", inputs: { playIcon: { classPropertyName: "playIcon", publicName: "playIcon", isSignal: true, isRequired: false, transformFunction: null }, pauseIcon: { classPropertyName: "pauseIcon", publicName: "pauseIcon", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "pointerdown": "onClick()" }, properties: { "attr.tip": "this.title" } }, ngImport: i0, template: "<svg\n viewBox=\"0 0 24 24\"\n [style.width]=\"'1.6rem'\"\n>\n <path\n fill=\"white\"\n [attr.d]=\"status$ | async\"\n />\n</svg>\n", styles: [""], dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: PlayComponent, decorators: [{ type: Component, args: [{ selector: 'telly-play', changeDetection: ChangeDetectionStrategy.OnPush, imports: [AsyncPipe], template: "<svg\n viewBox=\"0 0 24 24\"\n [style.width]=\"'1.6rem'\"\n>\n <path\n fill=\"white\"\n [attr.d]=\"status$ | async\"\n />\n</svg>\n" }] }], propDecorators: { playIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "playIcon", required: false }] }], pauseIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "pauseIcon", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], title: [{ type: HostBinding, args: ['attr.tip'] }], onClick: [{ type: HostListener, args: ['pointerdown'] }] } }); class RewindComponent { constructor() { this.icon = input(rewind, ...(ngDevMode ? [{ debugName: "icon" }] : [])); this.size = input('1.6rem', ...(ngDevMode ? [{ debugName: "size" }] : [])); this.player = inject(PlayerComponent); this.playing = false; this.handle = false; this.title = 'Rewind'; } onPointerDown() { this.handle = true; this.playing = this.player.media.playing; this.player.media.setRate(-4); } onPointerUpOrLeave() { if (this.handle) { this.player.media.setRate(1); this.playing ? this.player.media.play() : this.player.media.pause(); this.handle = false; } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: RewindComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.18", type: RewindComponent, isStandalone: true, selector: "telly-rewind", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "pointerdown": "onPointerDown()", "pointerup": "onPointerUpOrLeave()", "pointerleave": "onPointerUpOrLeave()" }, properties: { "attr.tip": "this.title" } }, ngImport: i0, template: "<svg\n viewBox=\"0 0 24 24\"\n [style.width]=\"size()\"\n>\n <path\n fill=\"white\"\n [attr.d]=\"icon()\"\n />\n</svg>\n", styles: [""], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: RewindComponent, decorators: [{ type: Component, args: [{ selector: 'telly-rewind', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<svg\n viewBox=\"0 0 24 24\"\n [style.width]=\"size()\"\n>\n <path\n fill=\"white\"\n [attr.d]=\"icon()\"\n />\n</svg>\n" }] }], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], title: [{ type: HostBinding, args: ['attr.tip'] }], onPointerDown: [{ type: HostListener, args: ['pointerdown'] }], onPointerUpOrLeave: [{ type: HostListener, args: ['pointerup'] }, { type: HostListener, args: ['pointerleave'] }] } }); class TimeComponent { constructor() { this.player = inject(PlayerComponent); this.events = inject(TellyEventsService); } ngOnInit() { const { media, timeOffset } = this.player; this.time$ = this.events.timer$.pipe(map(() => Math.floor(media.time + timeOffset())), distinctUntilChanged(), map((sec) => getTime(sec))); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: TimeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: TimeComponent, isStandalone: true, selector: "telly-time", ngImport: i0, template: "<p>{{ time$ | async }}</p>\n", styles: [""], dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: TimeComponent, decorators: [{ type: Component, args: [{ selector: 'telly-time', changeDetection: ChangeDetectionStrategy.OnPush, imports: [AsyncPipe], template: "<p>{{ time$ | async }}</p>\n" }] }] }); class ScrubBarComponent { constructor() { this.player = inject(PlayerComponent); this.ref = inject((ElementRef)); this.offset$ = new BehaviorSubject(0); this.time$ = new BehaviorSubject(0); this.hover$ = new BehaviorSubject(false); this.handle = false; } onPointerUp() { this.handle = false; } onPointerEnter() { this.hover$.next(true); } onPointerLeave() { this.handle = false; this.hover$.next(false); } onPointerDown(e) { this.handle = true; if (e.pointerType === 'mouse' && e.button != 0) { return; } const offset = e.offsetX / this.ref.nativeElement.clientWidth; this.player.media?.seek(this.player.media.duration * offset); } onPointerMove(e) { this.offset$.next(e.offsetX); this.time$.next(this.player.media.duration * (e.offsetX / this.ref.nativeElement.clientWidth)); if (this.handle) { const offset = e.offsetX / this.ref.nativeElement.clientWidth; this.player.media?.seek(this.player.media.duration * offset); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ScrubBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: ScrubBarComponent, isStandalone: true, selector: "telly-scrub-bar", host: { listeners: { "pointerup": "onPointerUp()", "pointerenter": "onPointerEnter()", "pointerleave": "onPointerLeave()", "pointerdown": "onPointerDown($event)", "pointermove": "onPointerMove($event)" } }, ngImport: i0, template: "<ng-content select=\"[out]\"></ng-content>\n<div class=\"outlet\">\n <ng-content select=\"[in]\"></ng-content>\n</div>\n", styles: [":host{display:flex;align-items:flex-end;position:relative;flex-grow:1;height:15px;border-radius:5px;box-shadow:inset -1px -1px 1px #e1a69280,inset 1px 1px 1px #00000080,inset 1px 1px 3px 3px #0000001a,inset -1px -1px 3px 3px #e1a6921a;cursor:pointer}.outlet{position:absolute;display:flex;justify-content:center;align-items:center;overflow:hidden;border-radius:5px;margin-left:1px;margin-bottom:1px;height:calc(100% - 2px);width:calc(100% - 2px)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ScrubBarComponent, decorators: [{ type: Component, args: [{ selector: 'telly-scrub-bar', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content select=\"[out]\"></ng-content>\n<div class=\"outlet\">\n <ng-content select=\"[in]\"></ng-content>\n</div>\n", styles: [":host{display:flex;align-items:flex-end;position:relative;flex-grow:1;height:15px;border-radius:5px;box-shadow:inset -1px -1px 1px #e1a69280,inset 1px 1px 1px #00000080,inset 1px 1px 3px 3px #0000001a,inset -1px -1px 3px 3px #e1a6921a;cursor:pointer}.outlet{position:absolute;display:flex;justify-content:center;align-items:center;overflow:hidden;border-radius:5px;margin-left:1px;margin-bottom:1px;height:calc(100% - 2px);width:calc(100% - 2px)}\n"] }] }], propDecorators: { onPointerUp: [{ type: HostListener, args: ['pointerup'] }], onPointerEnter: [{ type: HostListener, args: ['pointerenter'] }], onPointerLeave: [{ type: HostListener, args: ['pointerleave'] }], onPointerDown: [{ type: HostListener, args: ['pointerdown', ['$event']] }], onPointerMove: [{ type: HostListener, args: ['pointermove', ['$event']] }] } }); class ScrubBarCurrentTimeComponent { constructor() { this.player = inject(PlayerComponent); this.events = inject(TellyEventsService); } ngOnInit() { this.progress$ = this.events.timer$.pipe(map(() => this.getPercentage()), distinctUntilChanged()); } getPercentage() { return (this.player.media.windowTime / this.player.media.duration) * 100; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ScrubBarCurrentTimeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: ScrubBarCurrentTimeComponent, isStandalone: true, selector: "telly-scrub-bar-current-time", ngImport: i0, template: "<div\n class=\"current\"\n [style.width.%]=\"(progress$ | async)\"\n></div>\n", styles: [":host{position:absolute;width:100%;height:100%}.current{position:absolute;height:100%;filter:drop-shadow(0 0 5px rgba(244,134,22,.2));box-shadow:inset 1px -1px 1px #6565654d,inset -1px 1px 1px #f24c134d,inset -1px 1px 3px 3px #f24c131a,inset 1px -1px 3px 3px #cccccc1a;background:#f48616;background:-webkit-linear-gradient(to right,#F24C13,#F48616);background:linear-gradient(to right,#f24c13,#f48616);transition:width .1s ease}\n"], dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ScrubBarCurrentTimeComponent, decorators: [{ type: Component, args: [{ selector: 'telly-scrub-bar-current-time', changeDetection: ChangeDetectionStrategy.OnPush, imports: [AsyncPipe], template: "<div\n class=\"current\"\n [style.width.%]=\"(progress$ | async)\"\n></div>\n", styles: [":host{position:absolute;width:100%;height:100%}.current{position:absolute;height:100%;filter:drop-shadow(0 0 5px rgba(244,134,22,.2));box-shadow:inset 1px -1px 1px #6565654d,inset -1px 1px 1px #f24c134d,inset -1px 1px 3px 3px #f24c131a,inset 1px -1px 3px 3px #cccccc1a;background:#f48616;background:-webkit-linear-gradient(to right,#F24C13,#F48616);background:linear-gradient(to right,#f24c13,#f48616);transition:width .1s ease}\n"] }] }] }); class ScrubBarBufferedComponent { constructor() { this.player = inject(PlayerComponent); this.events = inject(TellyEventsService); } ngOnInit() { this.buffered$ = this.events.timer$.pipe(map(() => { const { buffered, drift, duration } = this.player.media; return [...Array(buffered.length)].map((_, idx) => ({ width: ((buffered.end(idx) - buffered.start(idx)) / duration) * 100, offset: ((buffered.start(idx) - drift) / duration) * 100, })); }), distinctUntilChanged((previous, current) => JSON.stringify(previous) === JSON.stringify(current))); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ScrubBarBufferedComponent, deps: [], target: i0.ɵɵFact