diff --git a/src/app/player/components/html-video-player/html-video-player.component.ts b/src/app/player/components/html-video-player/html-video-player.component.ts
index 5e755d045..eba6273f9 100644
--- a/src/app/player/components/html-video-player/html-video-player.component.ts
+++ b/src/app/player/components/html-video-player/html-video-player.component.ts
@@ -73,6 +73,8 @@ export class HtmlVideoPlayerComponent implements OnChanges, OnDestroy {
* Destroy hls instance on component destroy
*/
ngOnDestroy(): void {
- this.hls.destroy();
+ if (this.hls) {
+ this.hls.destroy();
+ }
}
}
diff --git a/src/app/player/components/info-overlay/info-overlay.component.html b/src/app/player/components/info-overlay/info-overlay.component.html
new file mode 100644
index 000000000..fa4a28f7e
--- /dev/null
+++ b/src/app/player/components/info-overlay/info-overlay.component.html
@@ -0,0 +1,36 @@
+
+
+
![]()
+
+
+
+ {{ epgProgram?.title[0]?.value }} | {{ channel.name }}
+
+
+ {{ epgProgram?.desc[0]?.value }}
+
+
+
+
+ {{ start | momentDate: 'YYYYMMDDHHmm ZZ':'HH:mm' }}
+ -
+ {{ stop | momentDate: 'YYYYMMDDHHmm ZZ':'HH:mm' }}
+
+
+
+
diff --git a/src/app/player/components/info-overlay/info-overlay.component.scss b/src/app/player/components/info-overlay/info-overlay.component.scss
new file mode 100644
index 000000000..6b302e27a
--- /dev/null
+++ b/src/app/player/components/info-overlay/info-overlay.component.scss
@@ -0,0 +1,73 @@
+.channel-overlay {
+ position: absolute;
+ bottom: 35px;
+ width: 100%;
+ background: #2c2b2fdc;
+ min-height: 130px;
+ max-height: 165px;
+ margin-left: auto;
+ margin-right: auto;
+ left: 0;
+ right: 0;
+ backdrop-filter: blur(10px);
+}
+
+.program-name,
+.program-description {
+ margin: 20px 0;
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.channel-logo {
+ margin-top: 20px;
+}
+
+.program-progress {
+ width: 96%;
+ background: #ccc;
+ height: 20px;
+ margin-bottom: 8px;
+}
+
+.program-name {
+ width: 96%;
+ max-height: 20px;
+ -webkit-line-clamp: 1;
+}
+
+.program-description {
+ width: 96%;
+ max-height: 55px;
+ -webkit-line-clamp: 3;
+}
+
+.program-finished {
+ background: #717171;
+ height: 20px;
+ font-size: 0.8em;
+ padding-left: 10px;
+ color: #000;
+ line-height: 20px;
+}
+
+.duration {
+ position: relative;
+ top: -18px;
+ font-size: 0.8em;
+ color: #000;
+ left: 10px;
+}
+
+.visible {
+ visibility: visible;
+ opacity: 1;
+}
+
+.hidden {
+ visibility: hidden;
+ opacity: 0;
+ transition: visibility 0s 2s, opacity 2s linear;
+}
diff --git a/src/app/player/components/info-overlay/info-overlay.component.spec.ts b/src/app/player/components/info-overlay/info-overlay.component.spec.ts
new file mode 100644
index 000000000..422157a8a
--- /dev/null
+++ b/src/app/player/components/info-overlay/info-overlay.component.spec.ts
@@ -0,0 +1,29 @@
+import { MockPipe } from 'ng-mocks';
+import { MomentDatePipe } from './../../../shared/pipes/moment-date.pipe';
+/* tslint:disable:no-unused-variable */
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { InfoOverlayComponent } from './info-overlay.component';
+
+describe('InfoOverlayComponent', () => {
+ let component: InfoOverlayComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(
+ waitForAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [InfoOverlayComponent, MockPipe(MomentDatePipe)],
+ }).compileComponents();
+ })
+ );
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(InfoOverlayComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/player/components/info-overlay/info-overlay.component.ts b/src/app/player/components/info-overlay/info-overlay.component.ts
new file mode 100644
index 000000000..371015b35
--- /dev/null
+++ b/src/app/player/components/info-overlay/info-overlay.component.ts
@@ -0,0 +1,76 @@
+import { Channel } from './../../../state/channel.model';
+import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
+import { EpgProgram } from '../../models/epg-program.model';
+import * as moment from 'moment';
+
+@Component({
+ selector: 'app-info-overlay',
+ templateUrl: './info-overlay.component.html',
+ styleUrls: ['./info-overlay.component.scss'],
+})
+export class InfoOverlayComponent implements OnChanges {
+ /** Active channel */
+ @Input() channel: Channel | undefined;
+
+ /** Current EPG program */
+ @Input() epgProgram: EpgProgram | undefined;
+
+ /** Visibility flag of the overlay popup */
+ isVisible = false;
+
+ /** Program duration */
+ generalDuration!: number;
+
+ /** Finished duration */
+ finishedDuration!: number;
+
+ /** Program start time */
+ start: moment.Moment;
+
+ /** Program end time */
+ stop: moment.Moment;
+
+ /** Timeout for the overlay visibility */
+ currentTimeout;
+
+ /**
+ * Calculates the necessary information for the visualization in the overview popup
+ * @param changes component input changes
+ */
+ ngOnChanges(changes: SimpleChanges): void {
+ if (changes.channel && !changes.channel.firstChange) {
+ clearTimeout(this.currentTimeout);
+ this.isVisible = true;
+ this.currentTimeout = setTimeout(() => {
+ this.isVisible = false;
+ }, 4000);
+ }
+ if (
+ changes.epgProgram &&
+ changes.epgProgram.currentValue &&
+ !changes.epgProgram.firstChange
+ ) {
+ const { stop, start } = changes.epgProgram.currentValue;
+ this.setProgramDuration(start, stop);
+ }
+ }
+
+ /**
+ * Calculates and sets the duration of the program for the progress bar visualization
+ * @param start program start time
+ * @param stop program stop time
+ */
+ setProgramDuration(start: number, stop: number): void {
+ this.stop = moment(stop, 'YYYYMMDDHHmm ZZ');
+ this.start = moment(start, 'YYYYMMDDHHmm ZZ');
+ const timeNow = moment(Date.now());
+
+ this.generalDuration = moment
+ .duration(this.stop.diff(this.start))
+ .asMilliseconds();
+
+ this.finishedDuration = moment
+ .duration(this.stop.diff(timeNow))
+ .asMilliseconds();
+ }
+}
diff --git a/src/app/player/components/video-player/video-player.component.html b/src/app/player/components/video-player/video-player.component.html
index 5d76a99a0..f3940f120 100644
--- a/src/app/player/components/video-player/video-player.component.html
+++ b/src/app/player/components/video-player/video-player.component.html
@@ -68,6 +68,12 @@
[channel]="activeChannel$ | async"
>
+
+
+
{
MockComponent(VjsPlayerComponent),
MockComponent(VideoPlayerComponent),
MockComponent(ChannelListContainerComponent),
+ MockComponent(InfoOverlayComponent),
VideoPlayerComponent,
MockPipe(TranslatePipe),
],
diff --git a/src/app/player/components/video-player/video-player.component.ts b/src/app/player/components/video-player/video-player.component.ts
index 19f1556cd..2c793b25c 100644
--- a/src/app/player/components/video-player/video-player.component.ts
+++ b/src/app/player/components/video-player/video-player.component.ts
@@ -1,3 +1,4 @@
+import { EpgProgram } from './../../models/epg-program.model';
import { Component, OnInit, ViewChild } from '@angular/core';
import { ChannelQuery, Channel, ChannelStore } from '../../../state';
import { Observable } from 'rxjs';
@@ -35,6 +36,11 @@ export class VideoPlayerComponent implements OnInit {
(store) => store.epgAvailable
);
+ /** Current epg program */
+ epgProgram$: Observable = this.channelQuery.select(
+ (store) => store.currentEpgProgram
+ );
+
/** Favorites list */
favorites$: Observable = this.channelQuery.select(
(store) => store.favorites
diff --git a/src/app/player/player.module.ts b/src/app/player/player.module.ts
index c30102efe..14f8eda2f 100644
--- a/src/app/player/player.module.ts
+++ b/src/app/player/player.module.ts
@@ -9,6 +9,7 @@ import { RouterModule, Routes } from '@angular/router';
import { SharedModule } from '../shared/shared.module';
import { EpgListComponent } from './components/epg-list/epg-list.component';
import { EpgItemDescriptionComponent } from './components/epg-list/epg-item-description/epg-item-description.component';
+import { InfoOverlayComponent } from './components/info-overlay/info-overlay.component';
const routes: Routes = [{ path: '', component: VideoPlayerComponent }];
@@ -19,6 +20,7 @@ const routes: Routes = [{ path: '', component: VideoPlayerComponent }];
EpgItemDescriptionComponent,
EpgListComponent,
EpgListItemComponent,
+ InfoOverlayComponent,
HtmlVideoPlayerComponent,
VideoPlayerComponent,
VjsPlayerComponent,