Merge pull request #5628 from tsiegleauq/vjs-404
catch and refresh unreachable video streams
This commit is contained in:
commit
d4b92a2b4e
@ -1,3 +1,14 @@
|
|||||||
<div class="video-wrapper">
|
<div class="video-wrapper">
|
||||||
|
<div class="player-container" [ngClass]="{ hide: !isUrlOnline }">
|
||||||
<video #videoPlayer class="video-js" controls preload="none"></video>
|
<video #videoPlayer class="video-js" controls preload="none"></video>
|
||||||
</div>
|
</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>
|
||||||
|
@ -1,6 +1,17 @@
|
|||||||
.video-wrapper {
|
.video-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
min-height: 200px;
|
||||||
|
|
||||||
|
.is-offline-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
margin: 1em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-container {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
.video-js {
|
.video-js {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
|
||||||
@ -37,3 +48,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { E2EImportsModule } from 'e2e-imports.module';
|
||||||
|
|
||||||
import { VjsPlayerComponent } from './vjs-player.component';
|
import { VjsPlayerComponent } from './vjs-player.component';
|
||||||
|
|
||||||
describe('VjsPlayerComponent', () => {
|
describe('VjsPlayerComponent', () => {
|
||||||
@ -8,7 +10,7 @@ describe('VjsPlayerComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [VjsPlayerComponent]
|
imports: [E2EImportsModule]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -10,6 +10,9 @@ import {
|
|||||||
ViewEncapsulation
|
ViewEncapsulation
|
||||||
} from '@angular/core';
|
} 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 videojs from 'video.js';
|
||||||
|
|
||||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
@ -32,14 +35,17 @@ enum MimeType {
|
|||||||
encapsulation: ViewEncapsulation.None
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class VjsPlayerComponent implements OnInit, OnDestroy {
|
export class VjsPlayerComponent implements OnInit, OnDestroy {
|
||||||
@ViewChild('videoPlayer', { static: true }) private videoPlayer: ElementRef;
|
@ViewChild('videoPlayer', { static: true })
|
||||||
|
private videoPlayer: ElementRef;
|
||||||
private _videoUrl: string;
|
private _videoUrl: string;
|
||||||
|
private posterUrl: string;
|
||||||
|
public player: videojs.Player;
|
||||||
|
public isUrlOnline: boolean;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
public set videoUrl(value: string) {
|
public set videoUrl(value: string) {
|
||||||
this._videoUrl = value;
|
this._videoUrl = value;
|
||||||
this.playVideo();
|
this.checkVideoUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
@ -49,8 +55,6 @@ export class VjsPlayerComponent implements OnInit, OnDestroy {
|
|||||||
return this._videoUrl;
|
return this._videoUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public player: videojs.Player;
|
|
||||||
|
|
||||||
private get videoSource(): VideoSource {
|
private get videoSource(): VideoSource {
|
||||||
return {
|
return {
|
||||||
src: this.videoUrl,
|
src: this.videoUrl,
|
||||||
@ -58,8 +62,6 @@ export class VjsPlayerComponent implements OnInit, OnDestroy {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private posterUrl: string;
|
|
||||||
|
|
||||||
public constructor(config: ConfigService) {
|
public constructor(config: ConfigService) {
|
||||||
config.get<string>('general_system_stream_poster').subscribe(posterUrl => {
|
config.get<string>('general_system_stream_poster').subscribe(posterUrl => {
|
||||||
this.posterUrl = posterUrl;
|
this.posterUrl = posterUrl;
|
||||||
@ -67,14 +69,7 @@ export class VjsPlayerComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async ngOnInit(): Promise<void> {
|
public async ngOnInit(): Promise<void> {
|
||||||
this.player = videojs(this.videoPlayer.nativeElement, {
|
this.initPlayer();
|
||||||
textTrackSettings: false,
|
|
||||||
fluid: true,
|
|
||||||
autoplay: 'any',
|
|
||||||
liveui: true,
|
|
||||||
poster: this.posterUrl
|
|
||||||
});
|
|
||||||
this.playVideo();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnDestroy(): void {
|
public ngOnDestroy(): void {
|
||||||
@ -83,12 +78,51 @@ export class VjsPlayerComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private playVideo(): void {
|
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) {
|
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.player.src(this.videoSource);
|
||||||
this.started.next();
|
this.started.next();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private determineContentTypeByUrl(url: string): MimeType {
|
private determineContentTypeByUrl(url: string): MimeType {
|
||||||
if (url) {
|
if (url) {
|
||||||
|
@ -774,3 +774,10 @@ button.mat-menu-item.selected {
|
|||||||
height: 0px;
|
height: 0px;
|
||||||
width: 0px;
|
width: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* simply hide something
|
||||||
|
*/
|
||||||
|
.hide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user