catch and refresh unreachable video streams
Detects if a live stream is reachable or not. Adds a refresh key to recheck if a stream can be reached Live adding and removing streams made easier
This commit is contained in:
parent
b9f78f501d
commit
b391ed0dfe
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}));
|
||||
|
||||
|
@ -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')) {
|
||||
|
@ -774,3 +774,10 @@ button.mat-menu-item.selected {
|
||||
height: 0px;
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
/**
|
||||
* simply hide something
|
||||
*/
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user