@ngx-telly/player
Version:
Telly - Angular frame-accurate HTML player
530 lines (518 loc) • 77.5 kB
JavaScript
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