Skip to content

Commit

Permalink
MVP WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
jptrsn committed Jan 28, 2025
1 parent bf6e7c8 commit 0d433c7
Show file tree
Hide file tree
Showing 29 changed files with 152 additions and 54 deletions.
2 changes: 1 addition & 1 deletion packages/client/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { windowControlsOverlaySelector } from './selectors/app.selector';
import { languageSelector, selectTranscriptionEnabled, themeSelector } from './selectors/settings.selector';
import { AuthActions } from './actions/auth.actions';
import { selectUserId } from './selectors/user.selector';
import { RecognitionActions } from './actions/recognition.actions';
import { RecognitionActions } from './actions/recogntion.actions';
import { selectUserLoggedIn } from './selectors/auth.selectors';
import { setDefaultDialect } from './actions/settings.actions';
import { StorageService } from './services/storage.service';
Expand Down
25 changes: 12 additions & 13 deletions packages/client/src/app/components/header/header.component.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<div class="bg-primary text-light gap-3 items-center p-2" [ngClass]="{'pwa-title-bar': windowControlsOverlay(), 'flex flex-row': !windowControlsOverlay()}" [@growshrink]="isActive() && !windowControlsOverlay()">

<!-- App Icon with return-to-home route -->
<button class="flex" [routerLink]="['']" [disabled]="activeRoute() === '/'">
<ng-icon name="zipCaptionsLogo" [size]="windowControlsOverlay() || (isActive() && !isBroadcasting()) ? '24' : '48'"></ng-icon>
</button>

<!-- App Title -->
<div class="flex-1 draggable" *ngIf="!isBroadcasting()">
<div class="normal-case text-xl hidden sm:inline" *ngIf="!isActive(); else smallTitle">ZipCaptions</div>
Expand All @@ -14,7 +14,7 @@
</div>

<!-- Transcript status badge-->
<button
<button
class="badge badge-info text-xs text-center sm:text-sm text-wrap h-auto"
[ngClass]="{'badge-info': isLoggedIn(), 'badge-warning': !isLoggedIn()}"
*ngIf="transcriptionEnabled() && !isViewingBroadcast()"
Expand All @@ -31,11 +31,10 @@
<span translate class="hidden sm:inline">TRANSCRIPT.loginRequired</span>
</ng-template>
</button>

<!-- Trigger recognition buttons -->
<ng-container *ngIf="activeRoute() !== '/stream'" [ngSwitch]="showRecordButton()">
<app-audio-input-enable *ngSwitchCase="true"></app-audio-input-enable>
<app-recognition-enable *ngSwitchCase="false"></app-recognition-enable>
<ng-container *ngIf="activeRoute() !== '/stream'">
<app-recognition-enable></app-recognition-enable>
<button *ngIf="!isActive() && activeRoute() === '/'" class="btn btn-circle btn-ghost tooltip tooltip-bottom" [ngClass]="{'btn-sm': windowControlsOverlay()}" [attr.data-tip]="'ROUTES.stream' | translate" routerLink="stream">
<ng-icon name="tablerBuildingBroadcastTower"></ng-icon>
</button>
Expand Down Expand Up @@ -74,13 +73,13 @@
<ng-container *ngTemplateOutlet="routeList;context:{items: item.children}"></ng-container>
</details>
</ng-container>

<ng-template #routeButton>

<button *ngIf="item.routerOutlet; else linkButton" [routerLink]="activeRoute() === item.routerOutlet ? null : item.routerOutlet" [ngClass]="{'text-lg': largeText}">
{{'ROUTES.' + item.label | translate }}
</button>

<ng-template #linkButton>
<a [href]="item.href" target="_blank" class="flex" [ngClass]="{'text-lg': largeText}">
<span class="flex basis-full">
Expand All @@ -89,12 +88,12 @@
<ng-icon name="heroArrowTopRightOnSquare"></ng-icon>
</a>
</ng-template>

</ng-template>

</li>
</ng-container>

<li *ngIf="topLevel">
<button *ngIf="loggedIn; else loginMenuItem" [ngClass]="{'text-lg': largeText}" (click)="logout()" translate>BUTTONS.logout</button>
<ng-template #loginMenuItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { lastValueFrom } from 'rxjs';
import { TestingModuleImports, TestingModuleProviders } from '../../../testing/test-scaffold';
import '../../../testing/matchMedia.mock';
import { AppPlatform, AppState } from '../../models/app.model';
import { RecognitionActions } from '../../actions/recognition.actions';
import { RecognitionActions } from '../../actions/recogntion.actions';
import { RecognitionEnableComponent } from '../../modules/media/components/recognition-enable/recognition-enable.component';
import { RecognitionRenderComponent } from '../../modules/media/components/recognition-render/recognition-render.component';
import { defaultAppState } from '../../reducers/app.reducer';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { By } from '@angular/platform-browser';
import { MockStore } from '@ngrx/store/testing';
import { AppState } from '../../models/app.model';
import { Store } from '@ngrx/store';
import { RecognitionActions } from '../../actions/recognition.actions';
import { RecognitionActions } from '../../actions/recogntion.actions';
import { recognitionStatusSelector } from '../../selectors/recognition.selector';
import { lastValueFrom } from 'rxjs';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { selectObsConnected, selectObsStreamActive, selectObsWebsocketIp } from
import { Store, select } from '@ngrx/store';
import { ObsActions } from '../../actions/obs.actions';
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import { RecognitionActions } from '../../actions/recognition.actions';
import { RecognitionActions } from '../../actions/recogntion.actions';
import { recognitionActiveSelector } from '../../selectors/recognition.selector';
import { filter, map, startWith } from 'rxjs';
import { AudioStreamActions } from '../../models/audio-stream.model';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<label class="form-control" [formGroup]="group">
<span class="text-accent label" translate>Engine</span>
<select class="select select-lg mt-1 w-full" formControlName="provider">
<option *ngFor="let p of providers" [value]="p.value">{{p.label}}</option>
</select>
</label>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RecognitionEngineSelectComponent } from './recognition-engine-select.component';

describe('RecognitionEngineSelectComponent', () => {
let component: RecognitionEngineSelectComponent;
let fixture: ComponentFixture<RecognitionEngineSelectComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [RecognitionEngineSelectComponent],
}).compileComponents();

fixture = TestBed.createComponent(RecognitionEngineSelectComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Component, Signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { select, Store, StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { RecognitionEngineState } from '../../models/recognition.model';
import { selectRecognitionEngine } from '../../selectors/recognition.selector';
import { map } from 'rxjs';
import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
import { AppState } from '../../models/app.model';
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { RecognitionActions } from '../../actions/recogntion.actions';

@Component({
selector: 'app-recognition-engine-select',
standalone: true,
imports: [
CommonModule,
StoreModule,
EffectsModule,
ReactiveFormsModule,
],
templateUrl: './recognition-engine-select.component.html',
styleUrls: ['./recognition-engine-select.component.scss'],
})
export class RecognitionEngineSelectComponent {
public provider: Signal<RecognitionEngineState['provider'] | undefined>;
public group: FormGroup;
public providers: { value: RecognitionEngineState['provider'], label: string }[] = [
{ value: 'web', label: 'Browser API (standard)' },
{ value: 'azure', label: 'Azure Cognitive Services (experimental)'}
]
constructor(private store: Store<AppState>,
private fb: FormBuilder
) {
this.provider = toSignal(this.store.pipe(
select(selectRecognitionEngine),
map((e) => e?.provider )
));
this.group = this.fb.group({
provider: this.fb.control(this.provider())
});
this.group.controls['provider'].valueChanges.pipe(
takeUntilDestroyed()
).subscribe((v) => {
this.store.dispatch(RecognitionActions.setEngine({ engine: v }))
})
}
}
2 changes: 1 addition & 1 deletion packages/client/src/app/effects/audio-stream.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Injectable } from "@angular/core";
import { Actions, createEffect, ofType } from "@ngrx/effects";
import { catchError, of, switchMap, tap } from "rxjs";
import { AudioStreamActions } from '../models/audio-stream.model';
import { RecognitionActions } from '../actions/recognition.actions';
import { RecognitionActions } from '../actions/recogntion.actions';
import { MediaService } from "../modules/media/services/media.service";

@Injectable()
Expand Down
33 changes: 23 additions & 10 deletions packages/client/src/app/effects/recognition.effects.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Inject, Injectable } from "@angular/core";
import { Actions, createEffect, ofType } from "@ngrx/effects";
import { catchError, from, map, of, switchMap } from "rxjs";
import { RecognitionActions } from '../actions/recognition.actions';
import { catchError, filter, from, map, of, switchMap } from "rxjs";
import { RecognitionActions } from '../actions/recogntion.actions';
import { AppActions } from "../models/app.model";
import { RecognitionService } from "../modules/media/services/recognition.service";
import { TranscriptionService } from "../modules/media/services/transcription.service";
Expand Down Expand Up @@ -107,15 +107,28 @@ export class RecognitionEffects {
)

deleteTranscriptDatabase$ = createEffect(() =>
this.actions$.pipe(
ofType(RecognitionActions.deleteTranscriptDB),
switchMap(() => this.transcription.deleteDatabase()
.pipe(
map(() => RecognitionActions.deleteTranscriptDBSuccess()),
catchError((err) => of(RecognitionActions.deleteTranscriptDBError({ error: err.message })))
)
)
this.actions$.pipe(
ofType(RecognitionActions.deleteTranscriptDB),
switchMap(() => this.transcription.deleteDatabase()
.pipe(
map(() => RecognitionActions.deleteTranscriptDBSuccess()),
catchError((err) => of(RecognitionActions.deleteTranscriptDBError({ error: err.message })))
)
)
)
)

initAzureRecognition$ = createEffect(() =>
this.actions$.pipe(
ofType(RecognitionActions.setEngine),
filter(({ engine }) => engine === 'azure'),
switchMap(() => this.recognitionService.initializeAzure()
.pipe(
map(({token, region}) => RecognitionActions.setEngineSuccess({token, region})),
catchError((err) => of(RecognitionActions.setEngineFailure({error: err})))
)
)
)
)

}
2 changes: 1 addition & 1 deletion packages/client/src/app/effects/user.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { AuthActions } from "../actions/auth.actions";
import { catchError, map, of, switchMap, tap } from "rxjs";
import { UserActions } from "../actions/user.actions";
import { SettingsActions } from "../modules/settings/models/settings.model";
import { RecognitionActions } from "../actions/recognition.actions";
import { RecognitionActions } from "../actions/recogntion.actions";

@Injectable()
export class UserEffects {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<button
class="btn btn-circle tooltip tooltip-left"
[ngClass]="{'btn-xs': connected(), 'btn-error': error(), 'btn-accent': disconnected(), 'btn-sm': small()}"
<button
class="btn btn-ghost btn-circle tooltip tooltip-left"
[ngClass]="{'btn-accent': disconnected(), 'btn-primary': connected(), 'btn-error': !!error(), 'btn-sm': small()}"
(click)="toggleState()"
appBackgroundMagnitude
appBackgroundMagnitude
[magnitude]="vol()"
[attr.data-tip]="connected() ? ('LABELS.stop' | translate) : ('LABELS.listen' | translate)"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import { Store, select } from '@ngrx/store';
import { TranslateService } from '@ngx-translate/core';
import { filter, of, switchMap } from 'rxjs';
import { AppState } from '../../../../models/app.model';
import { AudioStreamActions, AudioStreamState, AudioStreamStatus } from '../../../../models/audio-stream.model';
import { AudioStreamState, AudioStreamStatus } from '../../../../models/audio-stream.model';
import { errorSelector, windowControlsOverlaySelector } from '../../../../selectors/app.selector';
import { selectAudioStream } from '../../../../selectors/audio-stream.selector';
import { MediaService } from '../../services/media.service';
import { RecognitionActions } from '../../../../actions/recogntion.actions';

@Component({
selector: 'app-audio-input-enable',
Expand All @@ -30,7 +31,7 @@ export class AudioInputEnableComponent {
this.streamState = toSignal(this.store.pipe(select(selectAudioStream))) as Signal<AudioStreamState>;
this.connected = computed(() => this.streamState().status === AudioStreamStatus.connected);
this.disconnected = computed(() => this.streamState().status === AudioStreamStatus.disconnected);

const appError = toSignal(this.store.pipe(
select(errorSelector),
filter((err) => err !== 'ERRORS.liveTextMissing'),
Expand All @@ -44,12 +45,12 @@ export class AudioInputEnableComponent {

toggleState(): void {
if (this.connected() || this.error()) {
this.store.dispatch(AudioStreamActions.disconnectStream({id: this.streamState().id}))
this.store.dispatch(RecognitionActions.disconnect({id: this.streamState().id}))
} else {
if (this.router.url !== '/') {
this.router.navigate(['/'])
}
this.store.dispatch(AudioStreamActions.connectStream({id: this.streamState().id}))
this.store.dispatch(RecognitionActions.connect({id: this.streamState().id}))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { AppState } from '../../../../models/app.model';
import { selectLineHeight, selectRenderHistoryLength, selectTextSize } from '../../../../selectors/settings.selector';
import { AvailableLineHeights, AvailableTextSizes, LineHeight, SettingsActions, TextSize } from '../../../settings/models/settings.model';
import { selectIsBroadcasting } from '../../../../selectors/peer.selectors';
import { RecognitionActions } from '../../../../actions/recognition.actions';
import { RecognitionActions } from '../../../../actions/recogntion.actions';
import { recognitionConnectedSelector } from '../../../../selectors/recognition.selector';
import { selectObsConnected } from '../../../../selectors/obs.selectors';
import { ObsConnectionState } from '../../../../reducers/obs.reducer';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Router } from '@angular/router';
import { Store, select } from '@ngrx/store';
import { TranslateService } from '@ngx-translate/core';
import { filter, map, of, switchMap } from 'rxjs';
import { RecognitionActions } from '../../../../actions/recognition.actions';
import { RecognitionActions } from '../../../../actions/recogntion.actions';
import { AppState } from '../../../../models/app.model';
import { RecognitionStatus } from '../../../../models/recognition.model';
import { errorSelector, windowControlsOverlaySelector } from '../../../../selectors/app.selector';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { recognitionPausedSelector } from '../../../../selectors/recognition.sel
import { selectFontFamily, selectLineHeight, selectTextSize } from '../../../../selectors/settings.selector';
import { FontFamilyClassMap, LineHeight, TextSize } from '../../../settings/models/settings.model';
import { map } from 'rxjs';
import { RecognitionActions } from '../../../../actions/recognition.actions';
import { RecognitionActions } from '../../../../actions/recogntion.actions';

@Component({
selector: 'app-recognized-text',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { toSignal } from "@angular/core/rxjs-interop";
import { Store } from "@ngrx/store";
import * as sdk from "microsoft-cognitiveservices-speech-sdk";
import { map, Observable, take } from "rxjs";
import { RecognitionActions } from "../../../actions/recognition.actions";
import { RecognitionActions } from "../../../actions/recogntion.actions";
import { AppState } from "../../../models/app.model";
import { selectTranscriptionEnabled } from "../../../selectors/settings.selector";
import { InterfaceLanguage, RecognitionDialect } from "../../settings/models/settings.model";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Injectable, Signal, computed, effect } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { Store, select } from '@ngrx/store';
import { map } from 'rxjs';
import { RecognitionActions } from '../../../actions/recognition.actions';
import { map, Observable } from 'rxjs';
import { RecognitionActions } from '../../../actions/recogntion.actions';
import { AppState } from '../../../models/app.model';
import { ObsConnectionState } from '../../../reducers/obs.reducer';
import { selectObsConnected } from '../../../selectors/obs.selectors';
Expand Down Expand Up @@ -99,4 +99,8 @@ export class RecognitionService {
return this.azureRecognition.getRecognizedText();
}
}

initializeAzure(): Observable<{token: string; region: string}> {
return this.azureRecognition.initialize(this.language());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { toSignal } from '@angular/core/rxjs-interop';
import { Store, select } from '@ngrx/store';
import { BehaviorSubject, Subject, auditTime, debounceTime, delay, map, takeUntil, throttleTime, withLatestFrom } from 'rxjs';
import { ObsActions } from '../../../actions/obs.actions';
import { RecognitionActions } from '../../../actions/recognition.actions';
import { RecognitionActions } from '../../../actions/recogntion.actions';
import { AppPlatform, AppState } from '../../../models/app.model';
import { AudioStreamActions } from '../../../models/audio-stream.model';
import { SpeechRecognition } from '../../../models/recognition.model';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { slideInRightOnEnterAnimation, slideOutRightOnLeaveAnimation } from 'ang
import { Observable, Subject, takeUntil } from 'rxjs';
import { AppState } from '../../../../models/app.model';
import { RecognitionStatus } from '../../../../models/recognition.model';
import { RecognitionActions } from '../../../../actions/recognition.actions';
import { RecognitionActions } from '../../../../actions/recogntion.actions';
import { recognitionConnectedSelector, recognitionPausedSelector } from '../../../../selectors/recognition.selector';
import { RecognitionService } from '../../../media/services/recognition.service';
import { PeerService } from '../../services/peer.service';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { filter, map, take } from 'rxjs';
import { PeerActions } from '../../../../actions/peer.actions';
import { AppState } from '../../../../models/app.model';
import { RecognitionStatus } from '../../../../models/recognition.model';
import { RecognitionActions } from '../../../../actions/recognition.actions';
import { RecognitionActions } from '../../../../actions/recogntion.actions';
import { recognitionStatusSelector } from '../../../../selectors/recognition.selector';
import { AudioStreamState, AudioStreamStatus } from '../../../../models/audio-stream.model';
import { selectAudioStream } from '../../../../selectors/audio-stream.selector';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ObsActions } from '../../../../actions/obs.actions';
import { PeerActions } from '../../../../actions/peer.actions';
import { ComponentCanDeactivate } from '../../../../guards/active-stream/active-stream.guard';
import { AppPlatform, AppState } from '../../../../models/app.model';
import { RecognitionActions } from '../../../../actions/recognition.actions';
import { RecognitionActions } from '../../../../actions/recogntion.actions';
import { ObsConnectionState } from '../../../../reducers/obs.reducer';
import { peerConnectionsAcceptedSelector, platformSelector } from '../../../../selectors/app.selector';
import { selectObsConnected } from '../../../../selectors/obs.selectors';
Expand Down
Loading

0 comments on commit 0d433c7

Please sign in to comment.