Merge pull request #5628 from tsiegleauq/vjs-404

catch and refresh unreachable video streams
This commit is contained in:
Sean 2020-11-04 16:15:01 +01:00 committed by GitHub
commit d4b92a2b4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 114 additions and 48 deletions

View File

@ -1,3 +1,14 @@
<div class="video-wrapper">
<video #videoPlayer class="video-js" controls preload="none"></video>
<div class="player-container" [ngClass]="{ hide: !isUrlOnline }">
<video #videoPlayer class="video-js" controls preload="none"></video>
</div>
<div *ngIf="!isUrlOnline" class="is-offline-wrapper">
<div>
{{ 'No stream available' | translate }}
</div>
<button mat-raised-button (click)="checkVideoUrl()" color="primary">
<span>{{ 'Refresh' | translate }}</span>
<mat-icon>refresh</mat-icon>
</button>
</div>
</div>

View File

@ -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;
}
}
}
}

View File

@ -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();
}));

View File

@ -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<string>('general_system_stream_poster').subscribe(posterUrl => {
this.posterUrl = posterUrl;
@ -67,14 +69,7 @@ export class VjsPlayerComponent implements OnInit, OnDestroy {
}
public async ngOnInit(): Promise<void> {
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<void> {
/**
* 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')) {

View File

@ -774,3 +774,10 @@ button.mat-menu-item.selected {
height: 0px;
width: 0px;
}
/**
* simply hide something
*/
.hide {
display: none;
}