Skip to content

Commit

Permalink
Merge pull request #133 from jptrsn/azure-sr-followup
Browse files Browse the repository at this point in the history
Azure sr followup
  • Loading branch information
jptrsn authored Feb 16, 2025
2 parents 3e7dd6b + a6329b4 commit 4750c03
Show file tree
Hide file tree
Showing 34 changed files with 238 additions and 89 deletions.
5 changes: 3 additions & 2 deletions packages/client/src/app/actions/recogntion.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ export const RecognitionActions = createActionGroup({
'Delete Transcript DB Success': emptyProps(),
'Delete Transcript DB Error': props<{ error: string}>(),
'Set Engine': props<{engine: 'web' | 'azure' }>(),
'Set Engine Success': props<{token: string; region: string}>(),
'Set Engine Success': props<{engine: 'web' | 'azure' }>(),
'Set Engine Failure': props<{error: string}>(),
'Set Token': props<{token: string; region: string}>(),
'Load Engine': emptyProps(),
'Reset Engine': emptyProps(),
},
});
38 changes: 22 additions & 16 deletions packages/client/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,40 +16,42 @@ import { StoreModule } from '@ngrx/store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { TimeagoModule } from 'ngx-timeago';
import { Icons, SharedUiModule } from 'shared-ui';
import { AppComponent } from './app.component';
import { appRoutes } from './app.routes';
import { AboutComponent } from './components/about/about.component';
import { CookieModalComponent } from './components/cookie-modal/cookie-modal.component';
import { DetectPwaInstallComponent } from './components/detect-pwa-install/detect-pwa-install.component';
import { FooterComponent } from './components/footer/footer.component';
import { HeaderComponent } from './components/header/header.component';
import { HomeComponent } from './components/home/home.component';
import { NotFoundComponent } from './components/not-found/not-found.component';
import { ObsConnectionStatusComponent } from './components/obs-connection-status/obs-connection-status.component';
import { OfflineWarningComponent } from './components/offline-warning/offline-warning.component';
import { PrivacyComponent } from './components/privacy/privacy.component';
import { ServiceWorkerUpdateComponent } from './components/service-worker-update/service-worker-update.component';
import { SupporterRenderComponent } from './components/supporter-render/supporter-render.component';
import { TermsAndConditionsComponent } from './components/terms-and-conditions/terms-and-conditions.component';
import { WelcomeSplashComponent } from './components/welcome-splash/welcome-splash.component';
import { AppEffects } from './effects/app.effects';
import { AuthEffects } from './effects/auth.effects';
import { ObsEffects } from './effects/obs.effects';
import { PeerEffects } from './effects/peer.effects';
import { RecognitionEffects } from './effects/recognition.effects';
import { SettingsEffects } from './effects/settings.effects';
import { UserEffects } from './effects/user.effects';
import { JwtInterceptor } from './interceptors/jwt.interceptor';
import { MediaModule } from './modules/media/media.module';
import { provideExternalUrls } from './providers/external-url.provider';
import { appAppearanceReducers } from './reducers/app.reducer';
import { audioStreamReducers } from './reducers/audio-stream.reducer';
import { authReducer } from './reducers/auth.reducer';
import { obsReducers } from './reducers/obs.reducer';
import { peerReducers } from './reducers/peer.reducer';
import { recognitionReducers } from './reducers/recognition.reducer';
import { settingsReducers } from './reducers/settings.reducer';
import { TimeagoModule } from 'ngx-timeago';
import { SettingsEffects } from './effects/settings.effects';
import { RecognitionEffects } from './effects/recognition.effects';
import { ServiceWorkerUpdateComponent } from './components/service-worker-update/service-worker-update.component';
import { obsReducers } from './reducers/obs.reducer';
import { OfflineWarningComponent } from './components/offline-warning/offline-warning.component';
import { DetectPwaInstallComponent } from './components/detect-pwa-install/detect-pwa-install.component';
import { ObsConnectionStatusComponent } from './components/obs-connection-status/obs-connection-status.component';
import { JwtInterceptor } from './interceptors/jwt.interceptor';
import { authReducer } from './reducers/auth.reducer';
import { userReducer } from './reducers/user.reducer';
import { PeerEffects } from './effects/peer.effects';
import { ObsEffects } from './effects/obs.effects';
import { AuthEffects } from './effects/auth.effects';
import { UserEffects } from './effects/user.effects';
import { SupporterRenderComponent } from './components/supporter-render/supporter-render.component';

export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
Expand All @@ -71,6 +73,7 @@ export function HttpLoaderFactory(http: HttpClient) {
DetectPwaInstallComponent,
ObsConnectionStatusComponent,
SupporterRenderComponent,
NotFoundComponent,
],
imports: [
BrowserAnimationsModule,
Expand Down Expand Up @@ -124,7 +127,10 @@ export function HttpLoaderFactory(http: HttpClient) {
}),
TimeagoModule.forRoot(),
],
providers: [provideHttpClient(withInterceptors([JwtInterceptor]))],
providers: [
provideHttpClient(withInterceptors([JwtInterceptor])),
provideExternalUrls()
],
bootstrap: [AppComponent],
})
export class AppModule {}
10 changes: 6 additions & 4 deletions packages/client/src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@ import { HomeComponent } from './components/home/home.component';
import { PrivacyComponent } from './components/privacy/privacy.component';
import { TermsAndConditionsComponent } from './components/terms-and-conditions/terms-and-conditions.component';
import { CookiePolicyComponent } from './modules/settings/components/cookie-policy/cookie-policy.component';

import { NotFoundComponent } from './components/not-found/not-found.component';
import { externalUrlProvider } from './providers/external-url.provider';

export const appRoutes: Route[] = [
{ path: '', component: HomeComponent, data: { name: `home` } },
{ path: 'about', component: AboutComponent, data: { name: `about`}},
{ path: 'privacy', component: PrivacyComponent, data: { name: 'privacy'}},
{ path: 'privacy', component: PrivacyComponent, resolve: { url: externalUrlProvider }, data: { url: 'https://www.zipsolutions.org/privacy.html' }},
{ path: 'terms', component: TermsAndConditionsComponent, data: { name: 'terms'}},
{ path: 'cookies', component: CookiePolicyComponent, data: { name: 'cookies'}},
{ path: 'cookies', component: CookiePolicyComponent, resolve: { url: externalUrlProvider }, data: { url: 'https://www.zipsolutions.org/cookies.html' }},
{ path: 'settings', loadChildren: () => import('./modules/settings/settings.module').then(m => m.SettingsModule), data: { name: `settings`} },
{ path: 'stream', loadChildren: () => import('./modules/peer/peer.module').then(m =>m.PeerModule), data: { name: 'stream' }},
{ path: 'auth', loadChildren: () => import('./modules/auth/auth.module').then(m => m.AuthModule), data: { name: 'login' }},
{ path: 'user', loadChildren: () => import('./modules/user/user.module').then(m => m.UserModule), data: { name: 'account'}}
{ path: 'user', loadChildren: () => import('./modules/user/user.module').then(m => m.UserModule), data: { name: 'account'}},
{ path: '**', component: NotFoundComponent }
];
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
</div>
<div class="flex-initial flex-row">
<a [href]="licenseUrl" target="_blank" class="text-xs sm:text-sm ml-4 my-2 text-base-content text-right tooltip tooltip-left tooltip-info" [attr.data-tip]="('LABELS.build' | translate) + ': ' + buildTimestamp">
<span>© {{copyrightYear}}</span><span class="hidden sm:inline"> - Zip Captions</span>
<span>© {{copyrightYear}}</span><span class="hidden sm:inline"> - Zip Solutions</span>
</a>
</div>
</footer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class FooterComponent {
public error$: Signal<string | undefined>;
public activeRoute$: Signal<string | undefined>;
public repoUrl = 'https://github.com/jptrsn/zip-captions';
public licenseUrl = 'https://github.com/jptrsn/zip-captions/blob/main/LICENSE';
public licenseUrl = 'https://www.zipsolutions.org/';
public patreonUrl = 'https://patreon.com/zipcaptions';
public discordUrl = 'https://discord.gg/Swe2JeHnPc';
public copyrightYear = process.env['BUILD_YEAR'] || 2023;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div class="flex flex-col items-center justify-center w-full h-full">
<img src="assets/thinking.svg">
<div class="text-3xl">404</div>
<div class="text-lg" translate>ERRORS.pageNotFound</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
img {
max-width: 50%;
max-height: 512px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NotFoundComponent } from './not-found.component';

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

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

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

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Component } from '@angular/core';

@Component({
selector: 'app-not-found',
templateUrl: './not-found.component.html',
styleUrls: ['./not-found.component.scss'],
})
export class NotFoundComponent {}
6 changes: 3 additions & 3 deletions packages/client/src/app/effects/auth.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class AuthEffects {
ofType(AuthActions.login),
switchMap(() => this.authService.login()
.pipe(
map((id) => id ? AuthActions.loginSuccess({ id }) : AuthActions.logoutSuccess()),
switchMap((id) => id ? [AuthActions.loginSuccess({ id }), RecognitionActions.loadEngine()] : [AuthActions.logoutSuccess()]),
catchError((err) => of(AuthActions.loginFailure({ error: err.message})))
)
)
Expand All @@ -32,7 +32,7 @@ export class AuthEffects {
ofType(AuthActions.logout),
switchMap(() => this.authService.logout()
.pipe(
switchMap(() => [AuthActions.logoutSuccess(), UserActions.clearProfile(), RecognitionActions.setEngine({ engine: 'web' })]),
switchMap(() => [AuthActions.logoutSuccess(), UserActions.clearProfile(), RecognitionActions.resetEngine()]),
catchError((err) => of(AuthActions.logoutFailure({error: err.message})))
)
)
Expand All @@ -44,7 +44,7 @@ export class AuthEffects {
ofType(AuthActions.validate),
switchMap(() => this.authService.login()
.pipe(
map((id: string | null) => (id ? AuthActions.loginSuccess({ id }) : AuthActions.logoutSuccess())),
switchMap((id: string | null) => (id ? [AuthActions.loginSuccess({ id }), RecognitionActions.loadEngine()] : [AuthActions.logoutSuccess()])),
catchError(() => of(AuthActions.logoutSuccess()))
)
)
Expand Down
27 changes: 26 additions & 1 deletion packages/client/src/app/effects/recognition.effects.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { Inject, Injectable } from "@angular/core";
import { Actions, createEffect, ofType } from "@ngrx/effects";
import { catchError, filter, from, map, of, switchMap } from "rxjs";
import { catchError, 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";
import { StorageService } from "../services/storage.service";
import { SettingsState } from "../modules/settings/models/settings.model";
import { RecognitionState } from "../models/recognition.model";

@Injectable()
export class RecognitionEffects {
constructor(private actions$: Actions,
private storage: StorageService,
@Inject(RecognitionService) private recognitionService: RecognitionService,
@Inject(TranscriptionService) private transcription: TranscriptionService) {}

Expand Down Expand Up @@ -118,4 +122,25 @@ export class RecognitionEffects {
)
)

setEngine = createEffect(() =>
this.actions$.pipe(
ofType(RecognitionActions.setEngine),
map(({ engine }) => {
return engine;
}),
map((engine) => RecognitionActions.setEngineSuccess({ engine }))
)
)

loadEngine = createEffect(() =>
this.actions$.pipe(
ofType(RecognitionActions.loadEngine),
map(() => {
const cached: RecognitionState['engine']['provider'] | null = this.storage.get('recognitionEngine')
return cached ?? 'web'
}),
map((engine) => RecognitionActions.setEngineSuccess({ engine }))
)
)

}
10 changes: 1 addition & 9 deletions packages/client/src/app/effects/settings.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,7 @@ export class SettingsEffects {
)
)

saveProvider$ = createEffect(() =>
this.actions$.pipe(
ofType(RecognitionActions.setEngine),
map(({ engine }) => this.storage.update('settings', 'engine', engine)),
map(() => SettingsActions.saveEngineSuccess())
)
)

saveTranscriptionSettings$ = createEffect(() =>
saveTranscriptionSettings$ = createEffect(() =>
this.actions$.pipe(
ofType(SettingsActions.saveTranscriptionSettings),
withLatestFrom(this.store.select(selectTranscriptionSettings)),
Expand Down
4 changes: 1 addition & 3 deletions packages/client/src/app/models/recognition.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,8 @@ export interface RecognitionState {
}

export interface RecognitionEngineState {
loading?: boolean;
provider: 'web' | 'azure';
initialized: boolean;
token?: string;
region?: string;
}


Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<button
class="btn btn-ghost btn-circle tooltip tooltip-left"
<button
class="btn btn-circle tooltip tooltip-left relative"
[ngClass]="{'btn-accent': disconnected(), 'btn-primary': connected(), 'btn-error': !!error(), 'btn-sm': small()}"
(click)="toggleState()"
[attr.data-tip]="connected() ? ('LABELS.stop' | translate) : ('LABELS.listen' | translate)"
>
<ng-icon *ngIf="provider() !== 'web' && !connected()" class="absolute -top-2 -right-2 text-success rounded-full" size="24" name="heroSparkles"></ng-icon>
<ng-icon #errorIcon *ngIf="error(); else micEnabled" class="tooltip tooltip-warning tooltip-left" [attr.data-tip]="error()" name="heroExclamationTriangle"></ng-icon>
<ng-template #micEnabled>
<ng-container *ngIf="connected(); else inactive">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { TranslateService } from '@ngx-translate/core';
import { filter, map, of, switchMap } from 'rxjs';
import { RecognitionActions } from '../../../../actions/recogntion.actions';
import { AppState } from '../../../../models/app.model';
import { RecognitionStatus } from '../../../../models/recognition.model';
import { RecognitionEngineState, RecognitionStatus } from '../../../../models/recognition.model';
import { errorSelector, windowControlsOverlaySelector } from '../../../../selectors/app.selector';
import { recognitionErrorSelector, recognitionStatusSelector } from '../../../../selectors/recognition.selector';
import { recognitionErrorSelector, recognitionStatusSelector, selectRecognitionEngine } from '../../../../selectors/recognition.selector';

@Component({
selector: 'app-recognition-enable',
Expand All @@ -20,6 +20,7 @@ export class RecognitionEnableComponent {
public disconnected: Signal<boolean | undefined>;
public error: Signal<string | undefined>;
public small: Signal<boolean | undefined>;
public provider: Signal<RecognitionEngineState['provider'] | undefined>;

constructor(private store: Store<AppState>,
private translate: TranslateService,
Expand All @@ -40,6 +41,10 @@ export class RecognitionEnableComponent {
));
this.error = computed(() => recogError() || appError())
this.small = toSignal(this.store.select(windowControlsOverlaySelector));
this.provider = toSignal(this.store.pipe(
select(selectRecognitionEngine),
map((e) => e?.provider )
));
}

toggleState(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export class RecognitionService {
}

public connectToStream(): void {
console.log('connect to stream', this.activeLanguage()) // TODO: Remove after confirmming italian bug on mobile is no longer an issue
if (this.provider() === 'web') {
this.webRecognition.connectToStream();
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { HttpClient } from '@angular/common/http';
import { Inject, Injectable } from '@angular/core';
import { LocalDbService } from '../../../services/local-db/local-db.service';
import { firstValueFrom, from, Observable, Subject, tap } from 'rxjs';
import { Transcript, TranscriptTextSegment } from 'shared-ui';
import { TranslateService } from '@ngx-translate/core';
import { HttpClient } from '@angular/common/http';
import { firstValueFrom, from, Observable, tap } from 'rxjs';
import { Transcript, TranscriptTextSegment } from 'shared-ui';
import { LocalDbService } from '../../../services/local-db/local-db.service';

@Injectable({
providedIn: 'root'
Expand All @@ -19,7 +19,7 @@ export class TranscriptionService {
constructor(@Inject(LocalDbService) private localDb: LocalDbService,
@Inject(HttpClient) private http: HttpClient,
@Inject(TranslateService) private translate: TranslateService) {

const baseUrl = process.env['ZIP_AUTH_API_URL'] || 'http://localhost:3000'
const apiVersion = process.env['ZIP_AUTH_API_VERSION'] || 'v1';
const userRoute = process.env['ZIP_USER_API_ROUTE'] || 'user';
Expand Down Expand Up @@ -64,7 +64,7 @@ export class TranscriptionService {
async createTranscriptSegment(text: string, start: Date | undefined): Promise<number> {
if (this.transcriptId === undefined) {
throw new Error('No transcript ID set');
}
}
if (!this.lastTimestamp) {
throw new Error('No start timestamp defined');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,20 @@
</ng-container>

<ng-container *ngIf="provider() === 'azure'">
<div *ngIf="dialect() === 'unspecified'" class="text-md text-warn">WARNING! This engine requires a selected dialect. With your current settings, you will use the default dialect of {{fallbackDialect()}}</div>
<div *ngIf="dialect() === 'unspecified'" class="text-md text-warn"><span translate>SETTINGS.PROVIDER.dialectRequired</span> {{fallbackDialect()}}</div>
<app-dialect-selector [group]="group" controlName="dialect" [language]="language()"></app-dialect-selector>
</ng-container>
<div *ngIf="group.hasError('credits')" class="text-sm flex flex-row flex-wrap justify-between">
<span class="text-error" translate>SETTINGS.PROVIDER.acquire</span><a [href]="patreonUrl" target="_blank">{{patreonUrl}}</a>
</div>
<button class="btn btn-primary" type="button" (click)="setProvider()" translate>BUTTONS.save</button>
<button class="btn btn-primary" type="button" (click)="setProvider()" translate [disabled]="!group.dirty || group.invalid">BUTTONS.save</button>
</form>
</div>

<div class="toast z-10" @slideInUpOnEnter @fadeOutOnLeave *ngIf="showToast()">
<div class="alert alert-info" translate>SETTINGS.PROVIDER.setSuccess</div>
</div>

<ng-template #loginRequired>
<div class="badge badge-warning p-3 cursor-pointer" [routerLink]="['..','auth']" translate>SETTINGS.SHARING.loginRequired</div>
</ng-template>
Loading

0 comments on commit 4750c03

Please sign in to comment.