Skip to content

Commit

Permalink
feat(entry/live): allow editing manual live streams KMCNG-2476
Browse files Browse the repository at this point in the history
  • Loading branch information
amirch1 committed May 8, 2023
1 parent 1265ad8 commit be2aa5d
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {Injectable, OnDestroy} from '@angular/core';
import {BehaviorSubject} from 'rxjs';
import {asyncScheduler, BehaviorSubject, merge} from 'rxjs';
import { Observable, of as ObservableOf } from 'rxjs';
import { throwError } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
import {map, catchError, observeOn} from 'rxjs/operators';
import {
KalturaClient,
KalturaMultiRequest,
KalturaClient, KalturaLiveStreamConfiguration,
KalturaMultiRequest, KalturaPlaybackProtocol,
KalturaSipSourceType,
PexipGenerateSipUrlAction
} from 'kaltura-ngx-client';
Expand All @@ -26,13 +26,14 @@ import {KalturaConversionProfileFilter} from 'kaltura-ngx-client';
import {KalturaFilterPager} from 'kaltura-ngx-client';
import {KalturaConversionProfileType} from 'kaltura-ngx-client';
import {KalturaNullableBoolean} from 'kaltura-ngx-client';
import {AreaBlockerMessage} from '@kaltura-ng/kaltura-ui';
import {AreaBlockerMessage, KalturaValidators} from '@kaltura-ng/kaltura-ui';
import {BaseEntryGetAction} from 'kaltura-ngx-client';
import { KMCPermissions, KMCPermissionsService } from 'app-shared/kmc-shared/kmc-permissions';
import { ContentEntryViewSections } from 'app-shared/kmc-shared/kmc-views/details-views/content-entry-view.service';
import { LiveDashboardAppViewService } from 'app-shared/kmc-shared/kmc-views/component-views';
import {KalturaLogger} from '@kaltura-ng/kaltura-logger';
import { cancelOnDestroy, tag } from '@kaltura-ng/kaltura-common';
import {FormBuilder, FormGroup} from "@angular/forms";

export interface bitrate {
enabled: boolean,
Expand Down Expand Up @@ -65,6 +66,7 @@ export class EntryLiveWidget extends EntryWidget implements OnDestroy {
public _manualStreamsConfiguration = [];
public _bitrates: bitrate[] = [];
public _availableBitrates = AVAIL_BITRATES;
public _form: FormGroup;

public _autoStartOptions = [
{label: this._appLocalization.get('applications.content.entryDetails.live.disabled'), value: true},
Expand All @@ -77,10 +79,45 @@ export class EntryLiveWidget extends EntryWidget implements OnDestroy {
private _permissionsService: KMCPermissionsService,
private _browserService: BrowserService,
private _liveDasboardAppViewService: LiveDashboardAppViewService,
private _fb: FormBuilder,
logger: KalturaLogger) {
super(ContentEntryViewSections.Live, logger);
}

public initManualForm() {
this._form = this._fb.group({
flashHDSURL: ['', KalturaValidators.url],
hlsStreamUrl: ['', KalturaValidators.url],
dashStreamUrl: ['', KalturaValidators.url]
},
{
validator: (formGroup: FormGroup) => {
return this._atLeastOneUrlValidator(formGroup);
}
});

let flashHDSURL = this._manualStreamsConfiguration.find(stream => stream.label.split(" ")[0] === KalturaPlaybackProtocol.akamaiHds)?.url || '';
if (flashHDSURL === '') {
flashHDSURL = this._manualStreamsConfiguration.find(stream => stream.label.split(" ")[0] === KalturaPlaybackProtocol.hds)?.url || '';
}
const hlsStreamUrl = this._manualStreamsConfiguration.find(stream => stream.label.split(" ")[0] === KalturaPlaybackProtocol.appleHttp)?.url || '';
const dashStreamUrl = this._manualStreamsConfiguration.find(stream => stream.label.split(" ")[0] === KalturaPlaybackProtocol.mpegDash)?.url || '';
this._form.reset({ flashHDSURL, hlsStreamUrl, dashStreamUrl });

merge(this._form.valueChanges,
this._form.statusChanges)
.pipe(observeOn(asyncScheduler)) // using async scheduler so the form group status/dirty mode will be synchornized
.pipe(cancelOnDestroy(this))
.subscribe(
() => {
super.updateState({
isValid: this._form.status !== 'INVALID',
isDirty: this._form.dirty
});
}
);
}

protected onReset() {
this._DVRStatus = "";
this._manualLiveUrl = "";
Expand Down Expand Up @@ -118,8 +155,41 @@ export class EntryLiveWidget extends EntryWidget implements OnDestroy {
(data as KalturaLiveStreamEntry).conversionProfileId = this._selectedConversionProfile;
}
if (this._liveType === "manual") {
const entry = this.data as KalturaLiveStreamEntry;
(data as KalturaLiveStreamEntry).hlsStreamUrl = this._manualLiveUrl;
const entry = data as KalturaLiveStreamEntry;
entry.liveStreamConfigurations = [];
// save hls stream to main entry and stream configuration
const hlsStreamUrl = this._form.controls['hlsStreamUrl'].value;
entry.hlsStreamUrl = hlsStreamUrl;
if (hlsStreamUrl && hlsStreamUrl.length) {
const cfg = new KalturaLiveStreamConfiguration();
cfg.protocol = KalturaPlaybackProtocol.appleHttp;
cfg.url = hlsStreamUrl;
entry.liveStreamConfigurations.push(cfg);
}

// save flash stream to stream configuration
const flashStreamUrl = this._form.controls['flashHDSURL'].value;
if (flashStreamUrl && flashStreamUrl.length) {
let protocol = KalturaPlaybackProtocol.akamaiHds;
let flashStreamConfig = (this.data as KalturaLiveStreamEntry).liveStreamConfigurations.find(cfg => cfg.protocol === KalturaPlaybackProtocol.akamaiHds);
if (!flashStreamConfig) {
protocol = KalturaPlaybackProtocol.hds;
}
const cfg = new KalturaLiveStreamConfiguration();
cfg.protocol = protocol;
cfg.url = flashStreamUrl;
entry.liveStreamConfigurations.push(cfg);
}

// save dash stream to stream configuration
const dashStreamUrl = this._form.controls['dashStreamUrl'].value;
if (dashStreamUrl && dashStreamUrl.length) {
const dashStreamConfig = (this.data as KalturaLiveStreamEntry).liveStreamConfigurations.find(cfg => cfg.protocol === KalturaPlaybackProtocol.mpegDash);
const cfg = new KalturaLiveStreamConfiguration();
cfg.protocol = KalturaPlaybackProtocol.mpegDash;
cfg.url = dashStreamUrl;
entry.liveStreamConfigurations.push(cfg);
}
}
}

Expand Down Expand Up @@ -152,12 +222,34 @@ export class EntryLiveWidget extends EntryWidget implements OnDestroy {

protected onValidate(wasActivated: boolean): Observable<{ isValid: boolean}> {
return Observable.create(observer => {
const isValid = this._liveType === "universal" ? this._validateBitrates({updateDirtyMode: false}) : true;
observer.next({isValid});
let isValid = this._liveType === "universal" ? this._validateBitrates({updateDirtyMode: false}) : true;
if (this._liveType === "manual") {
for (const controlName in this._form.controls) {
if (this._form.controls.hasOwnProperty(controlName)) {
if (this._form.get(controlName).errors !== null) {
isValid = false;
}
}
}
if (this._form.errors !== null) {
isValid = false;
}
}
observer.next({isValid});
observer.complete()
});
}

private _atLeastOneUrlValidator(formgroup: FormGroup) {
if (!formgroup.controls['flashHDSURL'].value &&
!formgroup.controls['hlsStreamUrl'].value &&
!formgroup.controls['dashStreamUrl'].value) {
return {atLeastOneUrl: true};
} else {
return null;
}
}

protected onActivate(firstTimeActivating : boolean) {
// set live type and load data accordingly
switch (this.data.sourceType.toString()) {
Expand Down Expand Up @@ -228,6 +320,7 @@ export class EntryLiveWidget extends EntryWidget implements OnDestroy {
case KalturaSourceType.manualLiveStream.toString():
this._liveType = "manual";
this._setManualStreams();
this.initManualForm();
break;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,15 +303,66 @@
</div>

<div *ngIf="_widgetService._liveType === 'manual'">
<div class="kRow">
<span class="kWideLabels">{{'applications.content.entryDetails.live.hlsStream' | translate}}</span>
<input [(ngModel)]="_widgetService._manualLiveUrl" (ngModelChange)="_widgetService.setDirty()" pInputText class="urlInput"/>
</div>
<div class="kRow" *ngFor="let config of _widgetService._manualStreamsConfiguration">
<!-- loop through liveStreamConfigurations array -->
<span class="kWideLabels">{{config.label}}</span>
<span>{{config.url}}</span>
</div>
<form [formGroup]="_widgetService._form" novalidate>
<section class="kFormContainer">
<div class="kFormSection">
<div class="kRow">
<span class="kWideLabels">{{'applications.content.entryDetails.live.hlsStream' | translate}}</span>
<div class="inputWrapper">
<input class="kControl" formControlName="hlsStreamUrl" pInputText [ngClass]="{'kHasError':!_widgetService._form.controls['hlsStreamUrl'].valid && _widgetService._form.controls['hlsStreamUrl'].touched}">
<div *ngIf="_widgetService._form.controls['hlsStreamUrl'].touched">
<div *ngIf="_widgetService._form.controls['hlsStreamUrl'].hasError('url')" class="error">
{{'applications.upload.prepareLive.manualStreamType.errors.invalidUrl' | translate}}
</div>
</div>
</div>
</div>
</div>

<div class="kFormSection">
<div class="kRow">
<span class="kWideLabels">{{'applications.upload.prepareLive.manualStreamType.flashHDSURL' | translate}}</span>
<div class="inputWrapper">
<input class="kControl" formControlName="flashHDSURL" pInputText [ngClass]="{'kHasError':!_widgetService._form.controls['flashHDSURL'].valid && _widgetService._form.controls['flashHDSURL'].touched}">
<div *ngIf="_widgetService._form.controls['flashHDSURL'].touched">
<div *ngIf="_widgetService._form.controls['flashHDSURL'].hasError('url')" class="error">
{{'applications.upload.prepareLive.manualStreamType.errors.invalidUrl' | translate}}
</div>
</div>
</div>
</div>
</div>

<div class="kFormSection">
<div class="kRow">
<span class="kWideLabels">{{'applications.upload.prepareLive.manualStreamType.dashStreamUrl' | translate}}</span>
<div class="inputWrapper">
<input class="kControl" formControlName="dashStreamUrl" pInputText [ngClass]="{'kHasError':!_widgetService._form.controls['dashStreamUrl'].valid && _widgetService._form.controls['dashStreamUrl'].touched}">
<div *ngIf="_widgetService._form.controls['dashStreamUrl'].touched">
<div *ngIf="_widgetService._form.controls['dashStreamUrl'].hasError('url')" class="error">
{{'applications.upload.prepareLive.manualStreamType.errors.invalidUrl' | translate}}
</div>
</div>
</div>
</div>
</div>
</section>
</form>

<div class="kRow">
<div
*ngIf="_widgetService._form.touched && _widgetService._form.hasError('atLeastOneUrl')"
class="error">
{{'applications.upload.prepareLive.manualStreamType.errors.enterUrl' | translate}}
</div>
</div>

<div class="kRow" *ngFor="let config of _widgetService._manualStreamsConfiguration">
<!-- loop through unsupported liveStreamConfigurations array -->
<span *ngIf="!config.label.startsWith('applehttp') && !config.label.startsWith('hds') && !config.label.startsWith('mpegdash') && !config.label.startsWith('hdnetworkmanifest')" class="kWideLabels">{{config.label}}</span>
<span *ngIf="!config.label.startsWith('applehttp') && !config.label.startsWith('hds') && !config.label.startsWith('mpegdash') && !config.label.startsWith('hdnetworkmanifest')">{{config.url}}</span>
</div>

</div>
<span #anchor></span>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@
vertical-align: middle;
}
}
}
input.kControl, .inputWrapper {
width: 100%;
max-width: 500px;
}
}
.kCopyBtn{
Expand Down Expand Up @@ -95,6 +99,12 @@
text-overflow: ellipsis;
overflow: hidden;
}
.kHasError {
border-color: $kDandger;
}
.error {
margin-top: 4px;
}
.srtKeyInput {
width: 260px;
height: 34px;
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1898,7 +1898,7 @@
"srtPassManual": "Set Manually",
"srtPassPlaceholder": "Enter a passphrase",
"srtPassTooltip": "Set a passphrase to enable encryption\n10-79 alphanumeric character\npassphrase for encryption",
"hlsStream": "HLS stream URL:",
"hlsStream": "HLS (Apple HTTP) stream URL:",
"akamaiStream": "Akamai Stream ID:",
"transcoding": "Transcoding Profile Configuration",
"transcodingProfile": "Transcoding Profile:",
Expand Down

0 comments on commit be2aa5d

Please sign in to comment.