mirror of https://github.com/OpenVidu/openvidu.git
openvidu-components: Added captions
parent
370f7b015f
commit
2aec4b9fab
|
@ -11,7 +11,7 @@
|
|||
"@angular/platform-browser-dynamic": "13.3.9",
|
||||
"@angular/router": "13.3.9",
|
||||
"autolinker": "3.14.3",
|
||||
"lorem-ipsum": "^2.0.8",
|
||||
"openvidu-browser": "file:openvidu-browser-2.23.0.tgz",
|
||||
"openvidu-browser": "2.23.0",
|
||||
"rxjs": "7.5.6",
|
||||
"tslib": "2.3.1",
|
||||
|
|
|
@ -1,78 +1,193 @@
|
|||
.subtitles-container {
|
||||
.captions-container {
|
||||
/* padding: 5px; */
|
||||
display: flex;
|
||||
height: var(--ov-captions-height, 200px);
|
||||
height: var(--ov-captions-height, 230px);
|
||||
margin: 0px 10px;
|
||||
}
|
||||
.subtitles-offset {
|
||||
height: var(--ov-captions-height, 200px);
|
||||
.captions-offset {
|
||||
height: var(--ov-captions-height, 230px);
|
||||
width: 15%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.subtitles-main-container {
|
||||
flex-grow: 1;
|
||||
align-self: center;
|
||||
margin-left: 15px;
|
||||
max-height: var(--ov-captions-height, 200px);
|
||||
width: 70%;
|
||||
overflow: hidden;
|
||||
.captions-offset-xl {
|
||||
width: 25% !important;
|
||||
}
|
||||
|
||||
/* .subtitles-offset + .subtitles-offset {
|
||||
margin-left: 2%;
|
||||
} */
|
||||
#subtitle-settings-btn {
|
||||
.captions-center-container {
|
||||
flex-grow: 1;
|
||||
align-self: center;
|
||||
/* margin-left: 15px; */
|
||||
max-height: var(--ov-captions-height, 230px);
|
||||
width: 70%;
|
||||
overflow: hidden;
|
||||
|
||||
height: var(--ov-captions-height, 230px);
|
||||
padding: 0px 10vw 0px;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Screen XL
|
||||
*/
|
||||
.captions-center-container.screen-xl {
|
||||
padding: 0px 14vw;
|
||||
}
|
||||
|
||||
.captions-center-container.screen-xl .caption-event>span {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.captions-center-container.screen-xl #speaker {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* captions div*/
|
||||
.captions-center-container.screen-xl.events-one .caption-event {
|
||||
max-height: 140px;
|
||||
}
|
||||
|
||||
.captions-center-container.screen-xl.events-two .caption-event {
|
||||
max-height: 69px;
|
||||
}
|
||||
|
||||
.captions-center-container.screen-xl.events-three .caption-event {
|
||||
max-height: 35px;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Screen MD
|
||||
*/
|
||||
.captions-center-container.screen-md .caption-event>span {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.captions-center-container.screen-md #speaker {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* captions div*/
|
||||
.captions-center-container.screen-md.events-one .caption-event {
|
||||
max-height: 140px;
|
||||
}
|
||||
|
||||
.captions-center-container.screen-md.events-two .caption-event {
|
||||
max-height: 69px;
|
||||
}
|
||||
|
||||
.captions-center-container.screen-md.events-three .caption-event {
|
||||
max-height: 35px;
|
||||
}
|
||||
|
||||
/*
|
||||
* Screen SM
|
||||
*/
|
||||
|
||||
.captions-center-container.screen-sm {
|
||||
padding: 0px 2vw;
|
||||
}
|
||||
.captions-center-container.screen-sm .caption-event>span {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.captions-center-container.screen-sm #speaker {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* captions div*/
|
||||
|
||||
.captions-center-container.screen-sm.events-one .caption-event {
|
||||
max-height: 127px;
|
||||
}
|
||||
|
||||
.captions-center-container.screen-sm.events-two .caption-event {
|
||||
max-height: 64px;
|
||||
}
|
||||
|
||||
.captions-center-container.screen-sm.events-three .caption-event {
|
||||
max-height: 33px;
|
||||
}
|
||||
|
||||
/*
|
||||
* Screen XS
|
||||
*/
|
||||
|
||||
.captions-center-container.screen-xs {
|
||||
padding: 0px 2vw 0px;
|
||||
}
|
||||
|
||||
.captions-center-container.screen-xs .caption-event>span {
|
||||
font-size: 20px;
|
||||
}
|
||||
.captions-center-container.screen-xs #speaker {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* captions div*/
|
||||
.captions-center-container.screen-xs.events-one .caption-event {
|
||||
max-height: 130px;
|
||||
}
|
||||
|
||||
.captions-center-container.screen-xs.events-two .caption-event {
|
||||
max-height: 69px;
|
||||
}
|
||||
|
||||
.captions-center-container.screen-xs.events-three .caption-event {
|
||||
max-height: 35px;
|
||||
}
|
||||
|
||||
.captions-center-container .going-to-disappear{
|
||||
max-height: 30px !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#caption-settings-btn {
|
||||
color: var(--ov-text-color);
|
||||
background-color: var(--ov-secondary-color);
|
||||
}
|
||||
#subtitle-settings-icon {
|
||||
#caption-settings-icon {
|
||||
font-size: 15px;
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
#author {
|
||||
margin-bottom: 3px;
|
||||
#speaker {
|
||||
margin-bottom: 2px;
|
||||
font-weight: bold;
|
||||
/* padding: 5px; */
|
||||
margin-left: -5px;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#subtitle-text,
|
||||
#author {
|
||||
.captions-center-container .element {
|
||||
margin: 8px 0px;
|
||||
}
|
||||
|
||||
.caption-event {
|
||||
/* background-color: beige; */
|
||||
overflow: auto;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
.caption-text,
|
||||
#speaker {
|
||||
color: var(--ov-text-color);
|
||||
font-family: "Roboto",arial,sans-serif
|
||||
font-family: 'Roboto', arial, sans-serif;
|
||||
}
|
||||
|
||||
.subtitles-main-container .element {
|
||||
margin: 5px;
|
||||
}
|
||||
#subtitle-text {
|
||||
.caption-text {
|
||||
background-color: var(--ov-logo-background-color);
|
||||
padding: 4.5px;
|
||||
line-height: 1.6;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.big-text {
|
||||
font-size: 22px;
|
||||
}
|
||||
.medium-text {
|
||||
font-size: 20px;
|
||||
}
|
||||
.small-text {
|
||||
font-size: 18px;
|
||||
}
|
||||
.extra-small-text {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.big-author {
|
||||
font-size: 16px;
|
||||
}
|
||||
.medium-author {
|
||||
font-size: 14px;
|
||||
}
|
||||
.small-author {
|
||||
font-size: 12px;
|
||||
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -1,36 +1,40 @@
|
|||
<div class="subtitles-container" #captionsContainer>
|
||||
<div *ngIf="screenSize !== 'sm' && screenSize !== 'xs' && !settingsPanelOpened" class="subtitles-offset">
|
||||
<button (click)="onSettingsCliked()" id="subtitle-settings-btn" mat-flat-button>
|
||||
<div class="captions-container" #captionsContainer>
|
||||
<div
|
||||
*ngIf="captionsContainer.offsetWidth >= 600 && !settingsPanelOpened"
|
||||
class="captions-offset"
|
||||
[ngClass]="{ 'captions-offset': captionsContainer.offsetWidth >= 1000 }"
|
||||
>
|
||||
<button (click)="onSettingsCliked()" id="caption-settings-btn" mat-flat-button>
|
||||
<mat-icon id="subtitle-settings-icon">settings</mat-icon>
|
||||
<span>English</span>
|
||||
<span>{{ captionLangSelected.name }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="subtitles-main-container" #textContainer>
|
||||
<div class="element" *ngFor="let item of captionElements" #captionElement>
|
||||
<p
|
||||
id="author"
|
||||
<div
|
||||
class="captions-center-container"
|
||||
[ngClass]="{
|
||||
'big-author': captionsContainer.offsetWidth >= 1000 || screenSize === 'lg' || screenSize === 'xl',
|
||||
'medium-author': (captionsContainer.offsetWidth >= 960 && captionsContainer.offsetWidth < 1000) || screenSize === 'md',
|
||||
'small-author': captionsContainer.offsetWidth < 600 || screenSize === 'xs' || screenSize === 'sm'
|
||||
'events-one': captionEvents.length === 1,
|
||||
'events-two': captionEvents.length === 2,
|
||||
'events-three': captionEvents.length === 3,
|
||||
'screen-xl': captionsContainer.offsetWidth >= 1000,
|
||||
'screen-md': captionsContainer.offsetWidth >= 960 && captionsContainer.offsetWidth < 1000,
|
||||
'screen-sm': captionsContainer.offsetWidth >= 600 && captionsContainer.offsetWidth < 960,
|
||||
'screen-xs': captionsContainer.offsetWidth < 600
|
||||
}"
|
||||
>
|
||||
{{ item.author }} ({{item.text.length}})
|
||||
<div class="element" *ngFor="let caption of captionEvents; let i = index" @captionAnimation>
|
||||
<p id="speaker" [ngStyle]="{ color: caption.color }">
|
||||
{{ caption.nickname }}
|
||||
</p>
|
||||
<span
|
||||
id="subtitle-text"
|
||||
[ngClass]="{
|
||||
'big-text': captionsContainer.offsetWidth >= 1000 || screenSize === 'lg' || screenSize === 'xl',
|
||||
'medium-text': (captionsContainer.offsetWidth >= 960 && captionsContainer.offsetWidth < 1000) || screenSize === 'md',
|
||||
'small-text': (captionsContainer.offsetWidth >= 600 && captionsContainer.offsetWidth < 960) || screenSize === 'sm',
|
||||
'extra-small-text': captionsContainer.offsetWidth < 600 || screenSize === 'xs'
|
||||
}"
|
||||
>
|
||||
{{ item.text }}</span
|
||||
<div
|
||||
id="caption-event"
|
||||
class="caption-event"
|
||||
[ngClass]="{ 'going-to-disappear': i === 0 && captionEvents.length === MAX_EVENTS_LIMIT }"
|
||||
#captionEventElement
|
||||
>
|
||||
<span id="caption-text" class="caption-text">{{ caption.text }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="screenSize !== 'sm' && screenSize !== 'xs' && !settingsPanelOpened" class="subtitles-offset">
|
||||
</div>
|
||||
<div *ngIf="captionsContainer.offsetWidth >= 600 && !settingsPanelOpened" class="captions-offset"></div>
|
||||
</div>
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SubtitlesComponent } from './subtitles.component';
|
||||
import { CaptionsComponent } from './captions.component';
|
||||
|
||||
describe('SubtitlesComponent', () => {
|
||||
let component: SubtitlesComponent;
|
||||
let fixture: ComponentFixture<SubtitlesComponent>;
|
||||
describe('CaptionsComponent', () => {
|
||||
let component: CaptionsComponent;
|
||||
let fixture: ComponentFixture<CaptionsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ SubtitlesComponent ]
|
||||
declarations: [ CaptionsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SubtitlesComponent);
|
||||
fixture = TestBed.createComponent(CaptionsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -5,8 +5,8 @@
|
|||
.container {
|
||||
height: 100%;
|
||||
}
|
||||
.withSubtitles {
|
||||
height: calc(100% - var(--ov-captions-height, 200px)) !important;
|
||||
.withCaptions {
|
||||
height: calc(100% - var(--ov-captions-height, 250px)) !important;
|
||||
}
|
||||
|
||||
.layout {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div class="container" [class.withSubtitles]="subtitlesEnabled">
|
||||
<div class="container" [class.withCaptions]="captionsEnabled">
|
||||
<div id="layout" class="layout" #layout>
|
||||
<div *ngFor="let stream of localParticipant | streams" [ngClass]="{ OV_big: stream.videoEnlarged }" class="OT_root OT_publisher">
|
||||
<ng-container *ngTemplateOutlet="streamTemplate; context: { $implicit: stream }"></ng-container>
|
||||
|
@ -14,5 +14,5 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<ov-captions *ngIf="subtitlesEnabled" class="OV_ignored"></ov-captions>
|
||||
<ov-captions *ngIf="captionsEnabled" class="OV_ignored"></ov-captions>
|
||||
</div>
|
||||
|
|
|
@ -11,10 +11,10 @@ import {
|
|||
ViewContainerRef
|
||||
} from '@angular/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { ParticipantService } from '../../services/participant/participant.service';
|
||||
import { StreamDirective } from '../../directives/template/openvidu-angular.directive';
|
||||
import { ParticipantAbstractModel } from '../../models/participant.model';
|
||||
import { LayoutService } from '../../services/layout/layout.service';
|
||||
import { StreamDirective } from '../../directives/template/openvidu-angular.directive';
|
||||
import { ParticipantService } from '../../services/participant/participant.service';
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -82,11 +82,11 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
/**
|
||||
* @ignore
|
||||
*/
|
||||
subtitlesEnabled = true;
|
||||
captionsEnabled = true;
|
||||
|
||||
private localParticipantSubs: Subscription;
|
||||
private remoteParticipantsSubs: Subscription;
|
||||
private subtitlesSubs: Subscription;
|
||||
private captionsSubs: Subscription;
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
|
@ -95,7 +95,7 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
|
||||
ngOnInit(): void {
|
||||
this.subscribeToParticipants();
|
||||
this.subscribeToSubtitles();
|
||||
this.subscribeToCaptions();
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
|
@ -107,13 +107,13 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
this.remoteParticipants = [];
|
||||
if (this.localParticipantSubs) this.localParticipantSubs.unsubscribe();
|
||||
if (this.remoteParticipantsSubs) this.remoteParticipantsSubs.unsubscribe();
|
||||
if (this.subtitlesSubs) this.subtitlesSubs.unsubscribe();
|
||||
if (this.captionsSubs) this.captionsSubs.unsubscribe();
|
||||
this.layoutService.clear();
|
||||
}
|
||||
|
||||
private subscribeToSubtitles() {
|
||||
this.subtitlesSubs = this.layoutService.subtitlesTogglingObs.subscribe((value: boolean) => {
|
||||
this.subtitlesEnabled = value;
|
||||
private subscribeToCaptions() {
|
||||
this.captionsSubs = this.layoutService.captionsTogglingObs.subscribe((value: boolean) => {
|
||||
this.captionsEnabled = value;
|
||||
this.cd.markForCheck();
|
||||
this.layoutService.update();
|
||||
});
|
||||
|
|
|
@ -22,14 +22,14 @@
|
|||
<div mat-line>{{ 'PANEL.SETTINGS.AUDIO' | translate }}</div>
|
||||
</mat-list-option>
|
||||
<mat-list-option
|
||||
*ngIf="showSubtitles && false"
|
||||
*ngIf="showCaptions"
|
||||
class="option"
|
||||
[selected]="selectedOption === settingsOptions.SUBTITLES"
|
||||
[value]="settingsOptions.SUBTITLES"
|
||||
id="subtitles-opt"
|
||||
[selected]="selectedOption === settingsOptions.CAPTIONS"
|
||||
[value]="settingsOptions.CAPTIONS"
|
||||
id="captions-opt"
|
||||
>
|
||||
<mat-icon mat-list-icon>closed_caption</mat-icon>
|
||||
<div mat-line>{{ 'PANEL.SETTINGS.SUBTITLE' | translate }}</div>
|
||||
<div mat-line>{{ 'PANEL.SETTINGS.CAPTIONS' | translate }}</div>
|
||||
</mat-list-option>
|
||||
</mat-selection-list>
|
||||
</div>
|
||||
|
@ -47,7 +47,7 @@
|
|||
</div>
|
||||
<ov-video-devices-select *ngIf="selectedOption === settingsOptions.VIDEO"></ov-video-devices-select>
|
||||
<ov-audio-devices-select *ngIf="selectedOption === settingsOptions.AUDIO"></ov-audio-devices-select>
|
||||
<ov-subtitles-settings *ngIf="selectedOption === settingsOptions.SUBTITLES && showSubtitles && false"></ov-subtitles-settings>
|
||||
<ov-captions-settings *ngIf="selectedOption === settingsOptions.CAPTIONS && showCaptions"></ov-captions-settings>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -15,8 +15,8 @@ import { PanelEvent, PanelService } from '../../../services/panel/panel.service'
|
|||
export class SettingsPanelComponent implements OnInit {
|
||||
settingsOptions: typeof PanelSettingsOptions = PanelSettingsOptions;
|
||||
selectedOption: PanelSettingsOptions = PanelSettingsOptions.GENERAL;
|
||||
showSubtitles: boolean = true;
|
||||
private subtitlesSubs: Subscription;
|
||||
showCaptions: boolean = true;
|
||||
private captionsSubs: Subscription;
|
||||
panelSubscription: Subscription;
|
||||
constructor(private panelService: PanelService, private libService: OpenViduAngularConfigService) {}
|
||||
ngOnInit() {
|
||||
|
@ -25,7 +25,7 @@ export class SettingsPanelComponent implements OnInit {
|
|||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.subtitlesSubs) this.subtitlesSubs.unsubscribe();
|
||||
if (this.captionsSubs) this.captionsSubs.unsubscribe();
|
||||
}
|
||||
|
||||
close() {
|
||||
|
@ -36,8 +36,8 @@ export class SettingsPanelComponent implements OnInit {
|
|||
}
|
||||
|
||||
private subscribeToDirectives() {
|
||||
this.subtitlesSubs = this.libService.subtitlesButtonObs.subscribe((value: boolean) => {
|
||||
this.showSubtitles = value;
|
||||
this.captionsSubs = this.libService.captionsButtonObs.subscribe((value: boolean) => {
|
||||
this.showCaptions = value;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import {
|
||||
ChangeDetectionStrategy, ChangeDetectorRef, Component,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ContentChild,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
|
@ -12,8 +14,12 @@ import {
|
|||
} from '@angular/core';
|
||||
import {
|
||||
ConnectionEvent,
|
||||
RecordingEvent, Session, SessionDisconnectedEvent, StreamEvent,
|
||||
StreamPropertyChangedEvent, Subscriber
|
||||
RecordingEvent,
|
||||
Session,
|
||||
SessionDisconnectedEvent,
|
||||
StreamEvent,
|
||||
StreamPropertyChangedEvent,
|
||||
Subscriber
|
||||
} from 'openvidu-browser';
|
||||
|
||||
import { ILogger } from '../../models/logger.model';
|
||||
|
@ -26,6 +32,7 @@ import { SidenavMode } from '../../models/layout.model';
|
|||
import { PanelType } from '../../models/panel.model';
|
||||
import { Signal } from '../../models/signal.model';
|
||||
import { ActionService } from '../../services/action/action.service';
|
||||
import { CaptionService } from '../../services/caption/caption.service';
|
||||
import { ChatService } from '../../services/chat/chat.service';
|
||||
import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service';
|
||||
import { LayoutService } from '../../services/layout/layout.service';
|
||||
|
@ -46,11 +53,7 @@ import { TranslateService } from '../../services/translate/translate.service';
|
|||
selector: 'ov-session',
|
||||
templateUrl: './session.component.html',
|
||||
styleUrls: ['./session.component.css'],
|
||||
animations: [
|
||||
trigger('sessionAnimation', [
|
||||
transition(':enter', [style({ opacity: 0 }), animate('400ms', style({ opacity: 1 }))]),
|
||||
])
|
||||
],
|
||||
animations: [trigger('sessionAnimation', [transition(':enter', [style({ opacity: 0 }), animate('400ms', style({ opacity: 1 }))])])],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class SessionComponent implements OnInit {
|
||||
|
@ -77,6 +80,7 @@ export class SessionComponent implements OnInit {
|
|||
protected layoutWidthSubscription: Subscription;
|
||||
|
||||
protected updateLayoutInterval: NodeJS.Timer;
|
||||
private captionLanguageSubscription: Subscription;
|
||||
|
||||
protected log: ILogger;
|
||||
|
||||
|
@ -92,6 +96,7 @@ export class SessionComponent implements OnInit {
|
|||
protected panelService: PanelService,
|
||||
private recordingService: RecordingService,
|
||||
private translateService: TranslateService,
|
||||
private captionService: CaptionService,
|
||||
private platformService: PlatformService,
|
||||
private cd: ChangeDetectorRef
|
||||
) {
|
||||
|
@ -153,6 +158,7 @@ export class SessionComponent implements OnInit {
|
|||
}
|
||||
this.session = this.openviduService.getWebcamSession();
|
||||
this.sessionScreen = this.openviduService.getScreenSession();
|
||||
this.subscribeToCaptionLanguage();
|
||||
this.subscribeToConnectionCreatedAndDestroyed();
|
||||
this.subscribeToStreamCreated();
|
||||
this.subscribeToStreamDestroyed();
|
||||
|
@ -284,7 +290,7 @@ export class SessionComponent implements OnInit {
|
|||
}
|
||||
|
||||
private subscribeToStreamCreated() {
|
||||
this.session.on('streamCreated', (event: StreamEvent) => {
|
||||
this.session.on('streamCreated', async (event: StreamEvent) => {
|
||||
const connectionId = event.stream?.connection?.connectionId;
|
||||
const data = event.stream?.connection?.data;
|
||||
|
||||
|
@ -293,14 +299,39 @@ export class SessionComponent implements OnInit {
|
|||
const subscriber: Subscriber = this.session.subscribe(event.stream, undefined);
|
||||
this.participantService.addRemoteConnection(connectionId, data, subscriber);
|
||||
// this.oVSessionService.sendNicknameSignal(event.stream.connection);
|
||||
try {
|
||||
await this.session.subscribeToSpeechToText(event.stream, this.captionService.getLangSelected().ISO);
|
||||
} catch (error) {
|
||||
this.log.e('Error subscribing from STT: ', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private subscribeToStreamDestroyed() {
|
||||
this.session.on('streamDestroyed', (event: StreamEvent) => {
|
||||
this.session.on('streamDestroyed', async (event: StreamEvent) => {
|
||||
const connectionId = event.stream.connection.connectionId;
|
||||
this.participantService.removeConnectionByConnectionId(connectionId);
|
||||
try {
|
||||
await this.session.unsubscribeFromSpeechToText(event.stream);
|
||||
} catch (error) {
|
||||
this.log.e('Error unsubscribing from STT: ', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private subscribeToCaptionLanguage() {
|
||||
this.captionLanguageSubscription = this.captionService.captionLangObs.subscribe(async (lang) => {
|
||||
// Unsubscribe all streams from speech to text and re-subscribe with new language
|
||||
this.log.d('Re-subscribe from STT because of language changed to ', lang.ISO);
|
||||
for (const participant of this.participantService.getRemoteParticipants()) {
|
||||
try {
|
||||
await this.session.unsubscribeFromSpeechToText(participant.getCameraConnection().streamManager.stream);
|
||||
await this.session.subscribeToSpeechToText(participant.getCameraConnection().streamManager.stream, lang.ISO);
|
||||
} catch (error) {
|
||||
this.log.e('Error re-subscribing to STT: ', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,21 +1,18 @@
|
|||
<mat-list>
|
||||
<mat-list-item>
|
||||
<div mat-line>{{ 'PANEL.SETTINGS.SUBTITLE' | translate }}</div>
|
||||
<mat-slide-toggle (change)="toggleSubtitles()" [checked]="subtitlesEnabled" [disableRipple]="true"></mat-slide-toggle>
|
||||
<div mat-line>{{ 'PANEL.SETTINGS.CAPTIONS' | translate }}</div>
|
||||
<mat-slide-toggle (change)="toggleCaptions()" [checked]="captionsEnabled" [disableRipple]="true"></mat-slide-toggle>
|
||||
</mat-list-item>
|
||||
<mat-list-item>
|
||||
<div mat-line>{{ 'PANEL.SETTINGS.LANGUAGE' | translate }}:</div>
|
||||
<button mat-flat-button [matMenuTriggerFor]="menu" class="lang-button" [disabled]="true">
|
||||
<span>{{langSelected}}</span>
|
||||
<button mat-flat-button [matMenuTriggerFor]="menu" class="lang-button">
|
||||
<span>{{ langSelected }}</span>
|
||||
<mat-icon>expand_more</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item *ngFor="let lang of languagesAvailable" (click)="onLangSelected(lang)">
|
||||
<span>{{lang}}</span>
|
||||
<span>{{ lang.name }}</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CaptionsSettingComponent } from './captions.component';
|
||||
|
||||
describe('CaptionsSettingComponent', () => {
|
||||
let component: CaptionsSettingComponent;
|
||||
let fixture: ComponentFixture<CaptionsSettingComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [CaptionsSettingComponent]
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CaptionsSettingComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,47 @@
|
|||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { CaptionService } from '../../../services/caption/caption.service';
|
||||
import { LayoutService } from '../../../services/layout/layout.service';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ov-captions-settings',
|
||||
templateUrl: './captions.component.html',
|
||||
styleUrls: ['./captions.component.css']
|
||||
})
|
||||
export class CaptionsSettingComponent implements OnInit, OnDestroy {
|
||||
captionsEnabled: boolean;
|
||||
languagesAvailable: { name: string; ISO: string }[] = [];
|
||||
captionsSubscription: Subscription;
|
||||
langSelected: string;
|
||||
|
||||
constructor(private layoutService: LayoutService, private captionService: CaptionService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.subscribeToCaptions();
|
||||
this.langSelected = this.captionService.getLangSelected().name;
|
||||
this.languagesAvailable = this.captionService.getCaptionLanguages();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.captionsSubscription) this.captionsSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
onLangSelected(lang: { name: string; ISO: string }) {
|
||||
this.langSelected = lang.name;
|
||||
this.captionService.setLanguage(lang.ISO);
|
||||
}
|
||||
|
||||
toggleCaptions() {
|
||||
this.layoutService.toggleCaptions();
|
||||
}
|
||||
|
||||
private subscribeToCaptions() {
|
||||
this.captionsSubscription = this.layoutService.captionsTogglingObs.subscribe((value: boolean) => {
|
||||
this.captionsEnabled = value;
|
||||
// this.cd.markForCheck();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SubtitlesComponent } from './subtitles.component';
|
||||
|
||||
describe('SubtitlesComponent', () => {
|
||||
let component: SubtitlesComponent;
|
||||
let fixture: ComponentFixture<SubtitlesComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ SubtitlesComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SubtitlesComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -1,44 +0,0 @@
|
|||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { LayoutService } from '../../../services/layout/layout.service';
|
||||
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ov-subtitles-settings',
|
||||
templateUrl: './subtitles.component.html',
|
||||
styleUrls: ['./subtitles.component.css']
|
||||
})
|
||||
export class SubtitlesSettingComponent implements OnInit, OnDestroy {
|
||||
subtitlesEnabled: boolean;
|
||||
languagesAvailable = [];
|
||||
subtitlesSubs: Subscription;
|
||||
langSelected: string = 'English';
|
||||
|
||||
constructor(private layoutService: LayoutService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.subscribeToSubtitles();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.subtitlesSubs) this.subtitlesSubs.unsubscribe();
|
||||
}
|
||||
|
||||
onLangSelected(lang: string) {
|
||||
this.langSelected = lang;
|
||||
}
|
||||
|
||||
toggleSubtitles() {
|
||||
this.layoutService.toggleSubtitles();
|
||||
}
|
||||
|
||||
private subscribeToSubtitles() {
|
||||
this.subtitlesSubs = this.layoutService.subtitlesTogglingObs.subscribe((value: boolean) => {
|
||||
this.subtitlesEnabled = value;
|
||||
// this.cd.markForCheck();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -103,17 +103,17 @@
|
|||
<span>{{ 'TOOLBAR.BACKGROUND' | translate }}</span>
|
||||
</button>
|
||||
|
||||
<!-- Subtitles button -->
|
||||
<!-- Captions button -->
|
||||
<button
|
||||
*ngIf="!isMinimal && showSubtitlesButton && false"
|
||||
*ngIf="!isMinimal && showCaptionsButton"
|
||||
[disabled]="isConnectionLost"
|
||||
mat-menu-item
|
||||
id="subtitles-btn"
|
||||
(click)="toggleSubtitles()"
|
||||
id="captions-btn"
|
||||
(click)="toggleCaptions()"
|
||||
>
|
||||
<mat-icon>closed_caption</mat-icon>
|
||||
<span *ngIf="subtitlesEnabled">{{ 'TOOLBAR.DISABLE_SUBTITLES' | translate }}</span>
|
||||
<span *ngIf="!subtitlesEnabled">{{ 'TOOLBAR.ENABLE_SUBTITLES' | translate }}</span>
|
||||
<span *ngIf="captionsEnabled">{{ 'TOOLBAR.DISABLE_CAPTIONS' | translate }}</span>
|
||||
<span *ngIf="!captionsEnabled">{{ 'TOOLBAR.ENABLE_CAPTIONS' | translate }}</span>
|
||||
</button>
|
||||
|
||||
<mat-divider class="divider" *ngIf="!isMinimal && showSettingsButton"></mat-divider>
|
||||
|
|
|
@ -308,12 +308,12 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
/**
|
||||
* @ignore
|
||||
*/
|
||||
showSubtitlesButton: boolean = true;
|
||||
showCaptionsButton: boolean = true;
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
subtitlesEnabled: boolean;
|
||||
captionsEnabled: boolean;
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
|
@ -362,7 +362,7 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
private displaySessionNameSub: Subscription;
|
||||
private screenSizeSub: Subscription;
|
||||
private settingsButtonSub: Subscription;
|
||||
private subtitlesSubs: Subscription;
|
||||
private captionsSubs: Subscription;
|
||||
private currentWindowHeight = window.innerHeight;
|
||||
|
||||
/**
|
||||
|
@ -425,7 +425,7 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
this.subscribeToChatMessages();
|
||||
this.subscribeToRecordingStatus();
|
||||
this.subscribeToScreenSize();
|
||||
this.subscribeToSubtitlesToggling();
|
||||
this.subscribeToCaptionsToggling();
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
|
@ -453,7 +453,7 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
if (this.recordingSubscription) this.recordingSubscription.unsubscribe();
|
||||
if (this.screenSizeSub) this.screenSizeSub.unsubscribe();
|
||||
if (this.settingsButtonSub) this.settingsButtonSub.unsubscribe();
|
||||
if (this.subtitlesSubs) this.subtitlesSubs.unsubscribe();
|
||||
if (this.captionsSubs) this.captionsSubs.unsubscribe();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -547,8 +547,8 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
/**
|
||||
* @ignore
|
||||
*/
|
||||
toggleSubtitles() {
|
||||
this.layoutService.toggleSubtitles();
|
||||
toggleCaptions() {
|
||||
this.layoutService.toggleCaptions();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -697,8 +697,8 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
this.showSessionName = value;
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
this.subtitlesSubs = this.libService.subtitlesButtonObs.subscribe((value: boolean) => {
|
||||
this.showSubtitlesButton = value;
|
||||
this.captionsSubs = this.libService.captionsButtonObs.subscribe((value: boolean) => {
|
||||
this.showCaptionsButton = value;
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
}
|
||||
|
@ -710,9 +710,9 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
});
|
||||
}
|
||||
|
||||
private subscribeToSubtitlesToggling() {
|
||||
this.subtitlesSubs = this.layoutService.subtitlesTogglingObs.subscribe((value: boolean) => {
|
||||
this.subtitlesEnabled = value;
|
||||
private subscribeToCaptionsToggling() {
|
||||
this.captionsSubs = this.layoutService.captionsTogglingObs.subscribe((value: boolean) => {
|
||||
this.captionsEnabled = value;
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -27,5 +27,5 @@
|
|||
|
||||
/* Private css variables */
|
||||
#call-container {
|
||||
--ov-captions-height: 250px;
|
||||
--ov-captions-height: 230px;
|
||||
}
|
|
@ -56,12 +56,14 @@ import { TranslateService } from '../../services/translate/translate.service';
|
|||
* | :----------------------------: | :-------: | :---------------------------------------------: |
|
||||
* | **minimal** | `boolean` | {@link MinimalDirective} |
|
||||
* | **lang** | `string` | {@link LangDirective} |
|
||||
* | **captionsLang** | `string` | {@link CaptionsLangDirective} |
|
||||
* | **prejoin** | `boolean` | {@link PrejoinDirective} |
|
||||
* | **participantName** | `string` | {@link ParticipantNameDirective} |
|
||||
* | **videoMuted** | `boolean` | {@link VideoMutedDirective} |
|
||||
* | **audioMuted** | `boolean` | {@link AudioMutedDirective} |
|
||||
* | **toolbarScreenshareButton** | `boolean` | {@link ToolbarScreenshareButtonDirective} |
|
||||
* | **toolbarFullscreenButton** | `boolean` | {@link ToolbarFullscreenButtonDirective} |
|
||||
* | **toolbarCaptionsButton** | `boolean` | {@link ToolbarCaptionsButtonDirective} |
|
||||
* | **toolbarBackgroundEffectsButton** | `boolean` | {@link ToolbarBackgroundEffectsButtonDirective} |
|
||||
* | **toolbarLeaveButton** | `boolean` | {@link ToolbarLeaveButtonDirective} |
|
||||
* | **toolbarChatPanelButton** | `boolean` | {@link ToolbarChatPanelButtonDirective} |
|
||||
|
@ -457,8 +459,8 @@ export class VideoconferenceComponent implements OnInit, OnDestroy, AfterViewIni
|
|||
const publisher = await this.openviduService.initDefaultPublisher();
|
||||
|
||||
if (publisher) {
|
||||
publisher.once('accessDenied', (e: any) => {
|
||||
this.handlePublisherError(e);
|
||||
publisher.once('accessDenied', async (e: any) => {
|
||||
await this.handlePublisherError(e);
|
||||
resolve();
|
||||
});
|
||||
publisher.once('accessAllowed', async () => {
|
||||
|
@ -662,20 +664,20 @@ export class VideoconferenceComponent implements OnInit, OnDestroy, AfterViewIni
|
|||
this.onSessionCreated.emit(session);
|
||||
}
|
||||
|
||||
private handlePublisherError(e: any): Promise<void> {
|
||||
private async handlePublisherError(e: any): Promise<void> {
|
||||
let message: string = '';
|
||||
console.log('ERROR!', e);
|
||||
if (e.name === OpenViduErrorName.DEVICE_ALREADY_IN_USE) {
|
||||
this.log.w('Video device already in use. Disabling video device...');
|
||||
// Allow access to the room with only mic if camera device is already in use
|
||||
this.deviceSrv.disableVideoDevices();
|
||||
return this.initwebcamPublisher();
|
||||
return await this.initwebcamPublisher();
|
||||
}
|
||||
if (e.name === OpenViduErrorName.DEVICE_ACCESS_DENIED) {
|
||||
message = this.translateService.translate('ERRORS.MEDIA_ACCESS');
|
||||
this.deviceSrv.disableVideoDevices();
|
||||
this.deviceSrv.disableAudioDevices();
|
||||
return this.initwebcamPublisher();
|
||||
return await this.initwebcamPublisher();
|
||||
} else if (e.name === OpenViduErrorName.NO_INPUT_SOURCE_SET) {
|
||||
message = this.translateService.translate('ERRORS.DEVICE_NOT_FOUND');
|
||||
}
|
||||
|
|
|
@ -5,37 +5,39 @@ import { LogoDirective } from './internals.directive';
|
|||
import { ParticipantPanelItemMuteButtonDirective } from './participant-panel-item.directive';
|
||||
import { RecordingActivityRecordingErrorDirective, RecordingActivityRecordingsListDirective } from './recording-activity.directive';
|
||||
import {
|
||||
StreamDisplayParticipantNameDirective,
|
||||
StreamDisplayAudioDetectionDirective,
|
||||
StreamDisplayParticipantNameDirective,
|
||||
StreamSettingsButtonDirective
|
||||
} from './stream.directive';
|
||||
import {
|
||||
ToolbarScreenshareButtonDirective,
|
||||
ToolbarActivitiesPanelButtonDirective,
|
||||
ToolbarBackgroundEffectsButtonDirective,
|
||||
ToolbarCaptionsButtonDirective,
|
||||
ToolbarChatPanelButtonDirective,
|
||||
ToolbarDisplayLogoDirective,
|
||||
ToolbarDisplaySessionNameDirective,
|
||||
ToolbarFullscreenButtonDirective,
|
||||
ToolbarLeaveButtonDirective,
|
||||
ToolbarParticipantsPanelButtonDirective,
|
||||
ToolbarChatPanelButtonDirective,
|
||||
ToolbarDisplaySessionNameDirective,
|
||||
ToolbarDisplayLogoDirective,
|
||||
ToolbarActivitiesPanelButtonDirective,
|
||||
ToolbarBackgroundEffectsButtonDirective,
|
||||
ToolbarRecordingButtonDirective,
|
||||
ToolbarSettingsButtonDirective,
|
||||
ToolbarCaptionsButtonDirective
|
||||
ToolbarScreenshareButtonDirective,
|
||||
ToolbarSettingsButtonDirective
|
||||
} from './toolbar.directive';
|
||||
import {
|
||||
AudioMutedDirective,
|
||||
CaptionsLangDirective,
|
||||
LangDirective,
|
||||
MinimalDirective,
|
||||
PrejoinDirective,
|
||||
VideoMutedDirective,
|
||||
ParticipantNameDirective,
|
||||
LangDirective
|
||||
PrejoinDirective,
|
||||
VideoMutedDirective
|
||||
} from './videoconference.directive';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
MinimalDirective,
|
||||
LangDirective,
|
||||
CaptionsLangDirective,
|
||||
PrejoinDirective,
|
||||
VideoMutedDirective,
|
||||
AudioMutedDirective,
|
||||
|
@ -66,6 +68,7 @@ import {
|
|||
exports: [
|
||||
MinimalDirective,
|
||||
LangDirective,
|
||||
CaptionsLangDirective,
|
||||
PrejoinDirective,
|
||||
VideoMutedDirective,
|
||||
AudioMutedDirective,
|
||||
|
|
|
@ -6,7 +6,7 @@ import { Directive, ElementRef, HostListener, Input } from '@angular/core';
|
|||
* Load default OpenVidu logo if custom one is not exist
|
||||
* @internal
|
||||
*/
|
||||
@Directive({
|
||||
@Directive({
|
||||
selector: 'img[ovLogo]'
|
||||
})
|
||||
export class LogoDirective {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { AfterViewInit, Directive, ElementRef, HostListener, Input, OnDestroy } from '@angular/core';
|
||||
import { AfterViewInit, Directive, ElementRef, Input, OnDestroy } from '@angular/core';
|
||||
import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service';
|
||||
|
||||
/**
|
||||
|
@ -256,9 +256,6 @@ export class ToolbarBackgroundEffectsButtonDirective implements AfterViewInit, O
|
|||
* And it also can be used in the {@link ToolbarComponent}.
|
||||
* @example
|
||||
* <ov-toolbar [captionsButton]="false"></ov-toolbar>
|
||||
*
|
||||
* TODO: Make it public when speech to text is integrated
|
||||
* @internal
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'ov-videoconference[toolbarCaptionsButton], ov-toolbar[captionsButton]'
|
||||
|
@ -298,8 +295,8 @@ export class ToolbarCaptionsButtonDirective implements AfterViewInit, OnDestroy
|
|||
}
|
||||
|
||||
private update(value: boolean) {
|
||||
if (this.libService.subtitlesButton.getValue() !== value) {
|
||||
this.libService.subtitlesButton.next(value);
|
||||
if (this.libService.captionsButton.getValue() !== value) {
|
||||
this.libService.captionsButton.next(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { Directive, Input, ElementRef, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Directive, ElementRef, Input, OnDestroy, OnInit } from '@angular/core';
|
||||
import { CaptionService } from '../../services/caption/caption.service';
|
||||
import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service';
|
||||
import { TranslateService } from '../../services/translate/translate.service';
|
||||
|
||||
|
||||
/**
|
||||
* The **minimal** directive applies a minimal UI hiding all controls except for cam and mic.
|
||||
*
|
||||
|
@ -53,7 +55,7 @@ export class MinimalDirective implements OnDestroy {
|
|||
}
|
||||
|
||||
/**
|
||||
* The **lang** directive allows et the UI language to a default language.
|
||||
* The **lang** directive allows set the UI language to a default language.
|
||||
*
|
||||
* It is only available for {@link VideoconferenceComponent}.
|
||||
*
|
||||
|
@ -113,6 +115,70 @@ export class LangDirective implements OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The **captions-lang** directive allows specify the language of room's members
|
||||
*
|
||||
* It is only available for {@link VideoconferenceComponent}.
|
||||
*
|
||||
* It must be a valid [BCP-47](https://tools.ietf.org/html/bcp47) language tag like "en-US" or "es-ES".
|
||||
*
|
||||
*
|
||||
* **Default:** English `en-US`
|
||||
*
|
||||
* **Available:**
|
||||
*
|
||||
* * English: `en-US`
|
||||
* * Spanish: `es-ES`
|
||||
* * German: `de-DE`
|
||||
* * French: `fr-FR`
|
||||
* * Chinese: `zh-CN`
|
||||
* * Hindi: `hi-IN`
|
||||
* * Italian: `it-IT`
|
||||
* * Japanese: `jp-JP`
|
||||
* * Portuguese: `pt-PT`
|
||||
*
|
||||
* @example
|
||||
* <ov-videoconference [captionsLang]="es-ES"></ov-videoconference>
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'ov-videoconference[captionsLang]'
|
||||
})
|
||||
export class CaptionsLangDirective implements OnDestroy {
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
@Input() set captionsLang(value: string) {
|
||||
this.update(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
constructor(public elementRef: ElementRef, private captionService: CaptionService) {}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
clear() {
|
||||
this.update('en-US');
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
update(value: string) {
|
||||
this.captionService.setLanguage(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The **participantName** directive sets the participant name. It can be useful for aplications which doesn't need the prejoin page.
|
||||
*
|
||||
|
|
|
@ -34,8 +34,8 @@
|
|||
"MORE_OPTIONS":"更多选项",
|
||||
"FULLSCREEN":"全屏",
|
||||
"EXIT_FULLSCREEN": "退出全屏",
|
||||
"ENABLE_SUBTITLES": "启用字幕",
|
||||
"DISABLE_SUBTITLES": "禁用字幕",
|
||||
"ENABLE_CAPTIONS": "启用字幕",
|
||||
"DISABLE_CAPTIONS": "禁用字幕",
|
||||
"BACKGROUND":"背景效果",
|
||||
"START_RECORDING": "开始录音",
|
||||
"STOP_RECORDING": "停止录制",
|
||||
|
@ -73,7 +73,7 @@
|
|||
"VIDEO": "视频",
|
||||
"AUDIO": "声音的",
|
||||
"LANGUAGE": "语",
|
||||
"SUBTITLE": "字幕"
|
||||
"CAPTIONS": "字幕"
|
||||
},
|
||||
"BACKGROUND": {
|
||||
"TITLE": "背景效果",
|
||||
|
|
|
@ -34,8 +34,8 @@
|
|||
"MORE_OPTIONS": "Weitere Optionen",
|
||||
"FULLSCREEN": "Vollbild",
|
||||
"EXIT_FULLSCREEN": "Vollbildmodus beenden",
|
||||
"ENABLE_SUBTITLES": "Untertitel aktivieren",
|
||||
"DISABLE_SUBTITLES": "Untertitel deaktivieren",
|
||||
"ENABLE_CAPTIONS": "Untertitel aktivieren",
|
||||
"DISABLE_CAPTIONS": "Untertitel deaktivieren",
|
||||
"BACKGROUND": "Hintergrund-Effekte",
|
||||
"START_RECORDING": "Aufzeichnung starten",
|
||||
"STOP_RECORDING": "Aufzeichnung stoppen",
|
||||
|
@ -73,7 +73,7 @@
|
|||
"VIDEO": "Video",
|
||||
"AUDIO": "Audio",
|
||||
"LANGUAGE": "Sprache",
|
||||
"SUBTITLE": "Untertitel"
|
||||
"CAPTIONS": "Untertitel"
|
||||
},
|
||||
"BACKGROUND": {
|
||||
"TITLE": "Hintergrund-Effekte",
|
||||
|
|
|
@ -35,8 +35,8 @@
|
|||
"MORE_OPTIONS": "More options",
|
||||
"FULLSCREEN": "Fullscreen",
|
||||
"EXIT_FULLSCREEN": "Exit fullscreen",
|
||||
"ENABLE_SUBTITLES": "Enable subtitles",
|
||||
"DISABLE_SUBTITLES": "Disable subtitles",
|
||||
"ENABLE_CAPTIONS": "Enable captions",
|
||||
"DISABLE_CAPTIONS": "Disable captions",
|
||||
"BACKGROUND": "Background effects",
|
||||
"START_RECORDING": "Start recording",
|
||||
"STOP_RECORDING": "Stop recording",
|
||||
|
@ -74,7 +74,7 @@
|
|||
"VIDEO": "Video",
|
||||
"AUDIO": "Audio",
|
||||
"LANGUAGE": "Language",
|
||||
"SUBTITLE": "Subtitles"
|
||||
"CAPTIONS": "Captions"
|
||||
},
|
||||
"BACKGROUND": {
|
||||
"TITLE": "Background effects",
|
||||
|
|
|
@ -34,8 +34,8 @@
|
|||
"MORE_OPTIONS": "Más opciones",
|
||||
"EXIT_FULLSCREEN": "Quitar pantalla completa",
|
||||
"FULLSCREEN": "Pantalla completa",
|
||||
"ENABLE_SUBTITLES": "Activar subtítulos",
|
||||
"DISABLE_SUBTITLES": "Desactivar subtítulos",
|
||||
"ENABLE_CAPTIONS": "Activar subtítulos",
|
||||
"DISABLE_CAPTIONS": "Desactivar subtítulos",
|
||||
"BACKGROUND": "Efectos de fondo",
|
||||
"START_RECORDING": "Iniciar grabación",
|
||||
"STOP_RECORDING": "Detener grabación",
|
||||
|
@ -73,7 +73,7 @@
|
|||
"VIDEO": "Video",
|
||||
"AUDIO": "Audio",
|
||||
"LANGUAGE": "Idioma",
|
||||
"SUBTITLE": "Subtítulos"
|
||||
"CAPTIONS": "Subtítulos"
|
||||
},
|
||||
"BACKGROUND": {
|
||||
"TITLE": "Efectos de fondo",
|
||||
|
|
|
@ -34,8 +34,8 @@
|
|||
"MORE_OPTIONS": "Plus d'options",
|
||||
"FULLSCREEN": "Plein écran",
|
||||
"EXIT_FULLSCREEN": "Quitter le plein écran",
|
||||
"ENABLE_SUBTITLES": "Activer les sous-titres",
|
||||
"DISABLE_SUBTITLES": "Désactiver les sous-titres",
|
||||
"ENABLE_CAPTIONS": "Activer les sous-titres",
|
||||
"DISABLE_CAPTIONS": "Désactiver les sous-titres",
|
||||
"BACKGROUND": "Effets de fond",
|
||||
"START_RECORDING": "démarrer l'enregistrement",
|
||||
"STOP_RECORDING": "Arrêter l'enregistrement",
|
||||
|
@ -73,7 +73,7 @@
|
|||
"VIDEO": "Vidéo",
|
||||
"AUDIO": "l'audio",
|
||||
"LANGUAGE": "Langue",
|
||||
"SUBTITLE": "Les sous-titres"
|
||||
"CAPTIONS": "Les sous-titres"
|
||||
},
|
||||
"BACKGROUND": {
|
||||
"TITLE": "Effets de fond",
|
||||
|
|
|
@ -34,8 +34,8 @@
|
|||
"MORE_OPTIONS": "अधिक विकल्प",
|
||||
"FULLSCREEN": "पूर्ण स्क्रीन",
|
||||
"EXIT_FULLSCREEN": "पूर्ण स्क्रीन से बाहर निकलें",
|
||||
"ENABLE_SUBTITLES": "उपशीर्षक सक्षम करें",
|
||||
"DISABLE_SUBTITLES": "उपशीर्षक अक्षम करें",
|
||||
"ENABLE_CAPTIONS": "उपशीर्षक सक्षम करें",
|
||||
"DISABLE_CAPTIONS": "उपशीर्षक अक्षम करें",
|
||||
"BACKGROUND": "पृष्ठभूमि प्रभाव",
|
||||
"START_RECORDING": "रिकॉर्डिंग प्रारंभ करें",
|
||||
"STOP_RECORDING": "रिकॉर्डिंग रोकें",
|
||||
|
@ -73,7 +73,7 @@
|
|||
"VIDEO": "वीडियो",
|
||||
"AUDIO": "ऑडियो",
|
||||
"LANGUAGE": "भाषा",
|
||||
"SUBTITLE": "उपशीर्षक"
|
||||
"CAPTIONS": "उपशीर्षक"
|
||||
},
|
||||
"BACKGROUND": {
|
||||
"TITLE": "पृष्ठभूमि प्रभाव",
|
||||
|
|
|
@ -34,8 +34,8 @@
|
|||
"MORE_OPTIONS": "Altre opzioni",
|
||||
"FULLSCREEN": "Schermo intero",
|
||||
"EXIT_FULLSCREEN": "Esci dallo schermo intero",
|
||||
"ENABLE_SUBTITLES": "Abilita i sottotitoli",
|
||||
"DISABLE_SUBTITLES": "Disabilita i sottotitoli",
|
||||
"ENABLE_CAPTIONS": "Abilita i sottotitoli",
|
||||
"DISABLE_CAPTIONS": "Disabilita i sottotitoli",
|
||||
"BACKGROUND": "Effetti di sfondo",
|
||||
"START_RECORDING": "Avvia registrazione",
|
||||
"STOP_RECORDING": "Interrompi registrazione",
|
||||
|
@ -73,7 +73,7 @@
|
|||
"VIDEO": "video",
|
||||
"AUDIO": "Audio",
|
||||
"LANGUAGE":"Lingua",
|
||||
"SUBTITLE": "Sottotitoli"
|
||||
"CAPTIONS": "Sottotitoli"
|
||||
},
|
||||
"BACKGROUND": {
|
||||
"TITLE": "Effetti di sfondo",
|
||||
|
|
|
@ -34,8 +34,8 @@
|
|||
"MORE_OPTIONS": "その他のオプション",
|
||||
"FULLSCREEN": "フルスクリーン",
|
||||
"EXIT_FULLSCREEN": "フルスクリーンを終了する",
|
||||
"ENABLE_SUBTITLES": "字幕を有効にする",
|
||||
"DISABLE_SUBTITLES": "字幕を無効にする",
|
||||
"ENABLE_CAPTIONS": "字幕を有効にする",
|
||||
"DISABLE_CAPTIONS": "字幕を無効にする",
|
||||
"BACKGROUND": "背景効果",
|
||||
"START_RECORDING": "録画開始",
|
||||
"STOP_RECORDING": "録画の停止",
|
||||
|
@ -73,7 +73,7 @@
|
|||
"VIDEO": "ビデオ",
|
||||
"AUDIO": "オーディオ",
|
||||
"LANGUAGE":"言語",
|
||||
"SUBTITLE": "字幕"
|
||||
"CAPTIONS": "字幕"
|
||||
},
|
||||
"BACKGROUND": {
|
||||
"TITLE": "背景効果",
|
||||
|
|
|
@ -34,8 +34,8 @@
|
|||
"MORE_OPTIONS": "Meer opties",
|
||||
"FULLSCREEN": "Volledig scherm",
|
||||
"EXIT_FULLSCREEN": "Volledig scherm verlaten",
|
||||
"ENABLE_SUBTITLES": "Ondertiteling inschakelen",
|
||||
"DISABLE_SUBTITLES": "Ondertiteling uitschakelen",
|
||||
"ENABLE_CAPTIONS": "Ondertiteling inschakelen",
|
||||
"DISABLE_CAPTIONS": "Ondertiteling uitschakelen",
|
||||
"BACKGROUND": "Achtergrondeffecten",
|
||||
"START_RECORDING": "Start opname",
|
||||
"STOP_RECORDING": "Stop opname",
|
||||
|
@ -73,7 +73,7 @@
|
|||
"VIDEO": "Video",
|
||||
"AUDIO": "Audio",
|
||||
"LANGUAGE":"Taal",
|
||||
"SUBTITLE": "Ondertitels"
|
||||
"CAPTIONS": "Ondertitels"
|
||||
},
|
||||
"BACKGROUND": {
|
||||
"TITLE": "Achtergrondeffecten",
|
||||
|
|
|
@ -34,8 +34,8 @@
|
|||
"MORE_OPTIONS": "Mais opções",
|
||||
"FULLSCREEN": "Tela cheia",
|
||||
"EXIT_FULLSCREEN": "Sair da tela cheia",
|
||||
"ENABLE_SUBTITLES": "Ativar legendas",
|
||||
"DISABLE_SUBTITLES": "Desativar legendas",
|
||||
"ENABLE_CAPTIONS": "Ativar legendas",
|
||||
"DISABLE_CAPTIONS": "Desativar legendas",
|
||||
"BACKGROUND": "Efeitos de fundo",
|
||||
"START_RECORDING": "Iniciar_gravação",
|
||||
"STOP_RECORDING": "Parar de gravar",
|
||||
|
@ -73,7 +73,7 @@
|
|||
"VIDEO": "Vídeo",
|
||||
"AUDIO": "Áudio",
|
||||
"LANGUAGE":"Linguagem",
|
||||
"SUBTITLE": "Legendas"
|
||||
"CAPTIONS": "Legendas"
|
||||
},
|
||||
"BACKGROUND": {
|
||||
"TITLE": "Efeitos de fundo",
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
export interface CaptionModel {
|
||||
|
||||
connectionId: string;
|
||||
nickname: string;
|
||||
color: string;
|
||||
type: 'recognizing' | 'recognized';
|
||||
text: string;
|
||||
}
|
||||
|
|
@ -11,5 +11,5 @@ export enum PanelSettingsOptions {
|
|||
GENERAL = 'general',
|
||||
AUDIO = 'audio',
|
||||
VIDEO = 'video',
|
||||
SUBTITLES = 'subtitles'
|
||||
CAPTIONS = 'captions'
|
||||
}
|
||||
|
|
|
@ -7,5 +7,6 @@ export enum Storage {
|
|||
AUDIO_DEVICE = 'openviduCallAudioDevice',
|
||||
AUDIO_MUTED = 'openviduCallAudioMuted',
|
||||
VIDEO_MUTED = 'openviduCallVideoMuted',
|
||||
LANG = "openviduCallLang"
|
||||
LANG = 'openviduCallLang',
|
||||
CAPTION_LANG = 'openviduCallCaptionLang'
|
||||
}
|
|
@ -1,66 +1,66 @@
|
|||
import { OverlayContainer } from '@angular/cdk/overlay';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { ModuleWithProviders, NgModule } from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ModuleWithProviders, NgModule } from '@angular/core';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { OverlayContainer } from '@angular/cdk/overlay';
|
||||
|
||||
import { ToolbarComponent } from './components/toolbar/toolbar.component';
|
||||
import { VideoComponent } from './components/video/video.component';
|
||||
import { ChatPanelComponent } from './components/panel/chat-panel/chat-panel.component';
|
||||
import { SessionComponent } from './components/session/session.component';
|
||||
import { LayoutComponent } from './components/layout/layout.component';
|
||||
import { StreamComponent } from './components/stream/stream.component';
|
||||
import { DeleteDialogComponent } from './components/dialogs/delete-recording.component';
|
||||
import { DialogTemplateComponent } from './components/dialogs/dialog.component';
|
||||
import { RecordingDialogComponent } from './components/dialogs/recording-dialog.component';
|
||||
import { DeleteDialogComponent } from './components/dialogs/delete-recording.component';
|
||||
import { LayoutComponent } from './components/layout/layout.component';
|
||||
import { ChatPanelComponent } from './components/panel/chat-panel/chat-panel.component';
|
||||
import { SessionComponent } from './components/session/session.component';
|
||||
import { StreamComponent } from './components/stream/stream.component';
|
||||
import { ToolbarComponent } from './components/toolbar/toolbar.component';
|
||||
import { VideoComponent } from './components/video/video.component';
|
||||
|
||||
import { LinkifyPipe } from './pipes/linkify.pipe';
|
||||
import { TranslatePipe } from './pipes/translate.pipe';
|
||||
import { StreamTypesEnabledPipe, ParticipantStreamsPipe } from './pipes/participant.pipe';
|
||||
import { ParticipantStreamsPipe, StreamTypesEnabledPipe } from './pipes/participant.pipe';
|
||||
import { DurationFromSecondsPipe, SearchByStringPropertyPipe, ThumbnailFromUrlPipe } from './pipes/recording.pipe';
|
||||
import { TranslatePipe } from './pipes/translate.pipe';
|
||||
|
||||
import { OpenViduAngularConfig } from './config/openvidu-angular.config';
|
||||
import { CdkOverlayContainer } from './config/custom-cdk-overlay';
|
||||
import { DeviceService } from './services/device/device.service';
|
||||
import { LoggerService } from './services/logger/logger.service';
|
||||
import { PlatformService } from './services/platform/platform.service';
|
||||
import { StorageService } from './services/storage/storage.service';
|
||||
import { TokenService } from './services/token/token.service';
|
||||
import { OpenViduAngularConfigService } from './services/config/openvidu-angular.config.service';
|
||||
import { OpenViduService } from './services/openvidu/openvidu.service';
|
||||
import { OpenViduAngularConfig } from './config/openvidu-angular.config';
|
||||
import { ActionService } from './services/action/action.service';
|
||||
import { ChatService } from './services/chat/chat.service';
|
||||
import { OpenViduAngularConfigService } from './services/config/openvidu-angular.config.service';
|
||||
import { DeviceService } from './services/device/device.service';
|
||||
import { DocumentService } from './services/document/document.service';
|
||||
import { LayoutService } from './services/layout/layout.service';
|
||||
import { LoggerService } from './services/logger/logger.service';
|
||||
import { OpenViduService } from './services/openvidu/openvidu.service';
|
||||
import { PanelService } from './services/panel/panel.service';
|
||||
import { ParticipantService } from './services/participant/participant.service';
|
||||
import { PlatformService } from './services/platform/platform.service';
|
||||
import { RecordingService } from './services/recording/recording.service';
|
||||
import { StorageService } from './services/storage/storage.service';
|
||||
import { TokenService } from './services/token/token.service';
|
||||
|
||||
import { AudioWaveComponent } from './components/audio-wave/audio-wave.component';
|
||||
import { PanelComponent } from './components/panel/panel.component';
|
||||
import { ParticipantPanelItemComponent } from './components/panel/participants-panel/participant-panel-item/participant-panel-item.component';
|
||||
import { ParticipantsPanelComponent } from './components/panel/participants-panel/participants-panel/participants-panel.component';
|
||||
import { VideoconferenceComponent } from './components/videoconference/videoconference.component';
|
||||
import { PanelComponent } from './components/panel/panel.component';
|
||||
import { AudioWaveComponent } from './components/audio-wave/audio-wave.component';
|
||||
import { PreJoinComponent } from './components/pre-join/pre-join.component';
|
||||
import { VideoconferenceComponent } from './components/videoconference/videoconference.component';
|
||||
|
||||
import { AvatarProfileComponent } from './components/avatar-profile/avatar-profile.component';
|
||||
import { OpenViduAngularDirectiveModule } from './directives/template/openvidu-angular.directive.module';
|
||||
import { ApiDirectiveModule } from './directives/api/api.directive.module';
|
||||
import { BackgroundEffectsPanelComponent } from './components/panel/background-effects-panel/background-effects-panel.component';
|
||||
import { SettingsPanelComponent } from './components/panel/settings-panel/settings-panel.component';
|
||||
import { ActivitiesPanelComponent } from './components/panel/activities-panel/activities-panel.component';
|
||||
import { RecordingActivityComponent } from './components/panel/activities-panel/recording-activity-panel/recording-activity.component';
|
||||
import { AdminDashboardComponent } from './admin/dashboard/dashboard.component';
|
||||
import { AdminLoginComponent } from './admin/login/login.component';
|
||||
import { AppMaterialModule } from './openvidu-angular.material.module';
|
||||
import { VideoDevicesComponent } from './components/settings/video-devices/video-devices.component';
|
||||
import { AudioDevicesComponent } from './components/settings/audio-devices/audio-devices.component';
|
||||
import { NicknameInputComponent } from './components/settings/nickname-input/nickname-input.component';
|
||||
import { LangSelectorComponent } from './components/settings/lang-selector/lang-selector.component';
|
||||
import { SubtitlesSettingComponent } from './components/settings/subtitles/subtitles.component';
|
||||
import { AvatarProfileComponent } from './components/avatar-profile/avatar-profile.component';
|
||||
import { CaptionsComponent } from './components/captions/captions.component';
|
||||
import { ActivitiesPanelComponent } from './components/panel/activities-panel/activities-panel.component';
|
||||
import { RecordingActivityComponent } from './components/panel/activities-panel/recording-activity-panel/recording-activity.component';
|
||||
import { BackgroundEffectsPanelComponent } from './components/panel/background-effects-panel/background-effects-panel.component';
|
||||
import { SettingsPanelComponent } from './components/panel/settings-panel/settings-panel.component';
|
||||
import { AudioDevicesComponent } from './components/settings/audio-devices/audio-devices.component';
|
||||
import { CaptionsSettingComponent } from './components/settings/captions/captions.component';
|
||||
import { LangSelectorComponent } from './components/settings/lang-selector/lang-selector.component';
|
||||
import { NicknameInputComponent } from './components/settings/nickname-input/nickname-input.component';
|
||||
import { VideoDevicesComponent } from './components/settings/video-devices/video-devices.component';
|
||||
import { CustomBreakPointsProvider, CustomLayoutExtensionDirective } from './config/custom-flexlayout-breakpoints';
|
||||
import { ApiDirectiveModule } from './directives/api/api.directive.module';
|
||||
import { OpenViduAngularDirectiveModule } from './directives/template/openvidu-angular.directive.module';
|
||||
import { AppMaterialModule } from './openvidu-angular.material.module';
|
||||
|
||||
const publicComponents = [
|
||||
AdminDashboardComponent,
|
||||
|
@ -92,7 +92,7 @@ const privateComponents = [
|
|||
NicknameInputComponent,
|
||||
LangSelectorComponent,
|
||||
RecordingActivityComponent,
|
||||
SubtitlesSettingComponent
|
||||
CaptionsSettingComponent
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CaptionService } from './caption.service';
|
||||
|
||||
describe('CaptionService', () => {
|
||||
let service: CaptionService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(CaptionService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,47 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { StorageService } from '../storage/storage.service';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class CaptionService {
|
||||
private langTitles = [
|
||||
{ name: 'English', ISO: 'en-US' },
|
||||
{ name: 'Español', ISO: 'es-ES' },
|
||||
{ name: 'Deutsch', ISO: 'de-DE' },
|
||||
{ name: 'Français', ISO: 'fr-FR' },
|
||||
{ name: '中国', ISO: 'zh-CN' },
|
||||
{ name: 'हिन्दी', ISO: 'hi-IN' },
|
||||
{ name: 'Italiano', ISO: 'it-IT' },
|
||||
{ name: 'やまと', ISO: 'jp-JP' },
|
||||
{ name: 'Português', ISO: 'pt-PT' }
|
||||
];
|
||||
captionLangSelected: { name: string; ISO: string };
|
||||
captionLangObs: Observable<{ name: string; ISO: string }>;
|
||||
private _captionLangObs: Subject<{ name: string; ISO: string }> = new Subject();
|
||||
|
||||
|
||||
constructor(private storageService: StorageService) {
|
||||
this.captionLangObs = this._captionLangObs.asObservable();
|
||||
}
|
||||
|
||||
setLanguage(lang: string) {
|
||||
if (this.langTitles.some((l) => l.ISO === lang)) {
|
||||
this.captionLangSelected = this.langTitles.find((l) => l.ISO === lang);
|
||||
this._captionLangObs.next(this.captionLangSelected);
|
||||
this.storageService.setCaptionLang(lang);
|
||||
}
|
||||
}
|
||||
|
||||
getLangSelected(): { name: string; ISO: string } {
|
||||
return this.captionLangSelected || this.langTitles[0];
|
||||
}
|
||||
|
||||
getCaptionLanguages(): { name: string; ISO: string }[] {
|
||||
return this.langTitles;
|
||||
}
|
||||
}
|
|
@ -28,8 +28,8 @@ export class OpenViduAngularConfigService {
|
|||
fullscreenButton = <BehaviorSubject<boolean>>new BehaviorSubject(true);
|
||||
fullscreenButtonObs: Observable<boolean>;
|
||||
|
||||
subtitlesButton = <BehaviorSubject<boolean>>new BehaviorSubject(true);
|
||||
subtitlesButtonObs: Observable<boolean>;
|
||||
captionsButton = <BehaviorSubject<boolean>>new BehaviorSubject(true);
|
||||
captionsButtonObs: Observable<boolean>;
|
||||
|
||||
toolbarSettingsButton = <BehaviorSubject<boolean>>new BehaviorSubject(true);
|
||||
toolbarSettingsButtonObs: Observable<boolean>;
|
||||
|
@ -95,7 +95,7 @@ export class OpenViduAngularConfigService {
|
|||
this.displayLogoObs = this.displayLogo.asObservable();
|
||||
this.recordingButtonObs = this.recordingButton.asObservable();
|
||||
this.toolbarSettingsButtonObs = this.toolbarSettingsButton.asObservable();
|
||||
this.subtitlesButtonObs = this.subtitlesButton.asObservable();
|
||||
this.captionsButtonObs = this.captionsButton.asObservable();
|
||||
//Stream observables
|
||||
this.displayParticipantNameObs = this.displayParticipantName.asObservable();
|
||||
this.displayAudioDetectionObs = this.displayAudioDetection.asObservable();
|
||||
|
|
|
@ -13,17 +13,7 @@ export class DocumentService {
|
|||
screenSizeObs: Observable<MediaChange[]>;
|
||||
|
||||
constructor(private media: MediaObserver) {
|
||||
this.screenSizeObs= this.media.asObservable();
|
||||
}
|
||||
|
||||
getHTMLElementByClassName(element: HTMLElement, className: string): HTMLElement {
|
||||
while (!!element && element !== document.body) {
|
||||
if (element.className.includes(className)) {
|
||||
return element;
|
||||
}
|
||||
element = element.parentElement;
|
||||
}
|
||||
return null;
|
||||
this.screenSizeObs = this.media.asObservable();
|
||||
}
|
||||
|
||||
toggleFullscreen(elementId: string) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { LayoutAlignment, LayoutClass, OpenViduLayout, OpenViduLayoutOptions } from '../../models/layout.model';
|
||||
import { DocumentService } from '../document/document.service';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
|
@ -10,26 +9,24 @@ import { DocumentService } from '../document/document.service';
|
|||
providedIn: 'root'
|
||||
})
|
||||
export class LayoutService {
|
||||
|
||||
layoutContainer: HTMLElement | null = null;
|
||||
layoutWidthObs: Observable<number>;
|
||||
subtitlesTogglingObs: Observable<boolean>;
|
||||
captionsTogglingObs: Observable<boolean>;
|
||||
private layoutWidth: BehaviorSubject<number> = new BehaviorSubject(0);
|
||||
private openviduLayout: OpenViduLayout;
|
||||
private openviduLayoutOptions: OpenViduLayoutOptions;
|
||||
private subtitlesToggling: BehaviorSubject<boolean> = new BehaviorSubject(false);
|
||||
private captionsToggling: BehaviorSubject<boolean> = new BehaviorSubject(false);
|
||||
|
||||
|
||||
constructor(private documentService: DocumentService) {
|
||||
constructor() {
|
||||
this.layoutWidthObs = this.layoutWidth.asObservable();
|
||||
this.subtitlesTogglingObs = this.subtitlesToggling.asObservable();
|
||||
this.captionsTogglingObs = this.captionsToggling.asObservable();
|
||||
}
|
||||
|
||||
initialize(container: HTMLElement) {
|
||||
this.layoutContainer = container;
|
||||
this.openviduLayout = new OpenViduLayout();
|
||||
this.openviduLayoutOptions = this.getOptions();
|
||||
if(this.layoutContainer){
|
||||
if (this.layoutContainer) {
|
||||
this.openviduLayout.initLayoutContainer(this.layoutContainer, this.openviduLayoutOptions);
|
||||
}
|
||||
this.sendLayoutWidthEvent();
|
||||
|
@ -66,8 +63,8 @@ export class LayoutService {
|
|||
return options;
|
||||
}
|
||||
|
||||
toggleSubtitles() {
|
||||
this.subtitlesToggling.next(!this.subtitlesToggling.getValue());
|
||||
toggleCaptions() {
|
||||
this.captionsToggling.next(!this.captionsToggling.getValue());
|
||||
}
|
||||
|
||||
update(timeout: number = null) {
|
||||
|
@ -93,7 +90,7 @@ export class LayoutService {
|
|||
}
|
||||
|
||||
private sendLayoutWidthEvent() {
|
||||
const sidenavLayoutElement = this.documentService.getHTMLElementByClassName(
|
||||
const sidenavLayoutElement = this.getHTMLElementByClassName(
|
||||
this.openviduLayout?.getLayoutContainer(),
|
||||
LayoutClass.SIDENAV_CONTAINER
|
||||
);
|
||||
|
@ -101,4 +98,14 @@ export class LayoutService {
|
|||
this.layoutWidth.next(sidenavLayoutElement.clientWidth);
|
||||
}
|
||||
}
|
||||
|
||||
private getHTMLElementByClassName(element: HTMLElement | null, className: string): HTMLElement | null {
|
||||
while (!!element && element !== document.body) {
|
||||
if (element.className.includes(className)) {
|
||||
return element;
|
||||
}
|
||||
element = element.parentElement;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { ILogger } from '../../models/logger.model';
|
||||
import { LoggerService } from '../logger/logger.service';
|
||||
import { Storage } from '../../models/storage.model';
|
||||
import { LoggerService } from '../logger/logger.service';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
|
@ -62,6 +62,14 @@ export class StorageService {
|
|||
return this.get(Storage.LANG);
|
||||
}
|
||||
|
||||
setCaptionLang(lang: string){
|
||||
this.set(Storage.CAPTION_LANG, lang);
|
||||
}
|
||||
|
||||
getCaptionsLang(): string {
|
||||
return this.get(Storage.CAPTION_LANG);
|
||||
}
|
||||
|
||||
private set(key: string, item: any) {
|
||||
const value = JSON.stringify({ item: item });
|
||||
// this.log.d('Storing on localStorage "' + key + '" with value "' + value + '"');
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import * as cn from '../../lang/cn.json';
|
||||
import * as de from '../../lang/de.json';
|
||||
import * as en from '../../lang/en.json';
|
||||
import * as es from '../../lang/es.json';
|
||||
import * as de from '../../lang/de.json';
|
||||
import * as fr from '../../lang/fr.json';
|
||||
import * as cn from '../../lang/cn.json';
|
||||
import * as hi from '../../lang/hi.json';
|
||||
import * as ja from '../../lang/ja.json';
|
||||
import * as it from '../../lang/it.json';
|
||||
import * as ja from '../../lang/ja.json';
|
||||
import * as nl from '../../lang/nl.json';
|
||||
import * as pt from '../../lang/pt.json';
|
||||
import { StorageService } from '../storage/storage.service';
|
||||
|
@ -36,7 +36,7 @@ export class TranslateService {
|
|||
|
||||
constructor(private storageService: StorageService) {
|
||||
const iso = this.storageService.getLang() || 'en';
|
||||
this.langSelected = this.langTitles.find((lang) => lang.ISO === iso);
|
||||
this.langSelected = this.langTitles.find((lang) => lang.ISO === iso) || this.langTitles[0];
|
||||
this.currentLang = this.availableLanguages[this.langSelected.ISO];
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue