diff --git a/client/src/app/shared/components/vjs-player/vjs-player.component.html b/client/src/app/shared/components/vjs-player/vjs-player.component.html index bd0a96978..213a09bde 100644 --- a/client/src/app/shared/components/vjs-player/vjs-player.component.html +++ b/client/src/app/shared/components/vjs-player/vjs-player.component.html @@ -1,3 +1,14 @@
- +
+ +
+
+
+ {{ 'No stream available' | translate }} +
+ +
diff --git a/client/src/app/shared/components/vjs-player/vjs-player.component.scss b/client/src/app/shared/components/vjs-player/vjs-player.component.scss index c376df5c5..7b063c773 100644 --- a/client/src/app/shared/components/vjs-player/vjs-player.component.scss +++ b/client/src/app/shared/components/vjs-player/vjs-player.component.scss @@ -1,38 +1,50 @@ .video-wrapper { display: flex; width: 100%; - .video-js { - margin: auto; + min-height: 200px; - // we keep the button for now - // .vjs-big-play-button { - // left: 0; - // top: 0; - // width: 100%; - // height: 100%; - // border: 0; - // border-radius: 0; - // background-color: rgba(0, 0, 0, 0); - // .vjs-icon-placeholder { - // display: none !important; - // } - // } + .is-offline-wrapper { + width: 100%; + margin: 1em; + text-align: center; + } - .vjs-control-bar { - .vjs-subs-caps-button { - display: none !important; - } + .player-container { + height: 100%; + width: 100%; + .video-js { + margin: auto; - .vjs-descriptions-button { - display: none !important; - } + // we keep the button for now + // .vjs-big-play-button { + // left: 0; + // top: 0; + // width: 100%; + // height: 100%; + // border: 0; + // border-radius: 0; + // background-color: rgba(0, 0, 0, 0); + // .vjs-icon-placeholder { + // display: none !important; + // } + // } - .vjs-picture-in-picture-control { - display: none !important; - } + .vjs-control-bar { + .vjs-subs-caps-button { + display: none !important; + } - .vjs-audio-button { - display: none !important; + .vjs-descriptions-button { + display: none !important; + } + + .vjs-picture-in-picture-control { + display: none !important; + } + + .vjs-audio-button { + display: none !important; + } } } } diff --git a/client/src/app/shared/components/vjs-player/vjs-player.component.spec.ts b/client/src/app/shared/components/vjs-player/vjs-player.component.spec.ts index 36fc25132..4eefdb1a4 100644 --- a/client/src/app/shared/components/vjs-player/vjs-player.component.spec.ts +++ b/client/src/app/shared/components/vjs-player/vjs-player.component.spec.ts @@ -1,5 +1,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { E2EImportsModule } from 'e2e-imports.module'; + import { VjsPlayerComponent } from './vjs-player.component'; describe('VjsPlayerComponent', () => { @@ -8,7 +10,7 @@ describe('VjsPlayerComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [VjsPlayerComponent] + imports: [E2EImportsModule] }).compileComponents(); })); diff --git a/client/src/app/shared/components/vjs-player/vjs-player.component.ts b/client/src/app/shared/components/vjs-player/vjs-player.component.ts index cb6104b8c..7793364e8 100644 --- a/client/src/app/shared/components/vjs-player/vjs-player.component.ts +++ b/client/src/app/shared/components/vjs-player/vjs-player.component.ts @@ -10,6 +10,9 @@ import { ViewEncapsulation } from '@angular/core'; +import { of } from 'rxjs'; +import { ajax, AjaxResponse } from 'rxjs/ajax'; +import { catchError, map } from 'rxjs/operators'; import videojs from 'video.js'; import { ConfigService } from 'app/core/ui-services/config.service'; @@ -32,14 +35,17 @@ enum MimeType { encapsulation: ViewEncapsulation.None }) export class VjsPlayerComponent implements OnInit, OnDestroy { - @ViewChild('videoPlayer', { static: true }) private videoPlayer: ElementRef; - + @ViewChild('videoPlayer', { static: true }) + private videoPlayer: ElementRef; private _videoUrl: string; + private posterUrl: string; + public player: videojs.Player; + public isUrlOnline: boolean; @Input() public set videoUrl(value: string) { this._videoUrl = value; - this.playVideo(); + this.checkVideoUrl(); } @Output() @@ -49,8 +55,6 @@ export class VjsPlayerComponent implements OnInit, OnDestroy { return this._videoUrl; } - public player: videojs.Player; - private get videoSource(): VideoSource { return { src: this.videoUrl, @@ -58,8 +62,6 @@ export class VjsPlayerComponent implements OnInit, OnDestroy { }; } - private posterUrl: string; - public constructor(config: ConfigService) { config.get('general_system_stream_poster').subscribe(posterUrl => { this.posterUrl = posterUrl; @@ -67,14 +69,7 @@ export class VjsPlayerComponent implements OnInit, OnDestroy { } public async ngOnInit(): Promise { - this.player = videojs(this.videoPlayer.nativeElement, { - textTrackSettings: false, - fluid: true, - autoplay: 'any', - liveui: true, - poster: this.posterUrl - }); - this.playVideo(); + this.initPlayer(); } public ngOnDestroy(): void { @@ -83,13 +78,52 @@ export class VjsPlayerComponent implements OnInit, OnDestroy { } } - private playVideo(): void { - if (this.player) { - this.player.src(this.videoSource); - this.started.next(); + public async checkVideoUrl(): Promise { + /** + * Using observable would not make sense, because without it would not automatically update + * if a Ressource switches from online to offline + */ + const ajaxResponse: AjaxResponse = await ajax(this.videoUrl) + .pipe( + map(response => response), + catchError(error => { + return of(error); + }) + ) + .toPromise(); + + /** + * there is no enum for http status codes in the whole Angular stack... + */ + if (ajaxResponse.status === 200) { + this.isUrlOnline = true; + this.playVideo(); + } else { + this.isUrlOnline = false; + if (this.player) { + this.player.pause(); + } + this.player.src(''); } } + private initPlayer(): void { + if (!this.player) { + this.player = videojs(this.videoPlayer.nativeElement, { + textTrackSettings: false, + fluid: true, + autoplay: 'any', + liveui: true, + poster: this.posterUrl + }); + } + } + + private playVideo(): void { + this.player.src(this.videoSource); + this.started.next(); + } + private determineContentTypeByUrl(url: string): MimeType { if (url) { if (url.startsWith('rtmp')) { diff --git a/client/src/styles.scss b/client/src/styles.scss index 7449851c7..666823d4d 100644 --- a/client/src/styles.scss +++ b/client/src/styles.scss @@ -774,3 +774,10 @@ button.mat-menu-item.selected { height: 0px; width: 0px; } + +/** + * simply hide something + */ +.hide { + display: none; +}