Skip to content

Commit

Permalink
OV-61: * conflicts resolved
Browse files Browse the repository at this point in the history
  • Loading branch information
XCODE89 committed Sep 20, 2024
2 parents 2e42c99 + 55a0416 commit 5273374
Show file tree
Hide file tree
Showing 151 changed files with 12,769 additions and 5,846 deletions.
8 changes: 8 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ AWS_SECRET_ACCESS_KEY=see-in-slack
AWS_S3_REGION=eu-north-1
AWS_S3_BUCKET_NAME=bsa-2024-outreachvids
AWS_CLOUDFRONT_DOMAIN_ID=d2tm5q3cg1nlwf
AWS_CLOUDFRONT_DOMAIN_ID_FOR_RENDERED_VIDEO=SOME_SECRET_KEY

#
# OPEN AI
Expand All @@ -48,3 +49,10 @@ ORIGIN=http://localhost:3000
AZURE_SUBSCRIPTION_KEY=see-in-slack
AZURE_SERVICE_REGION=see-in-slack
AZURE_SERVICE_ENDPOINT=see-in-slack

#
# REMOTION
#
REMOTION_LAMBDA_FUNCTION_NAME=SOME_SECRET_KEY
REMOTION_SERVE_URL=SOME_SECRET_KEY
REMOTION_BUCKET_NAME=SOME_SECRET_KEY
1 change: 1 addition & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"@fastify/static": "7.0.4",
"@fastify/swagger": "8.15.0",
"@fastify/swagger-ui": "4.0.1",
"@remotion/lambda": "4.0.201",
"bcrypt": "5.1.1",
"convict": "6.2.4",
"dotenv": "16.4.5",
Expand Down
6 changes: 3 additions & 3 deletions backend/src/bundles/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
BaseController,
} from '~/common/controller/controller.js';
import { ApiPath } from '~/common/enums/enums.js';
import { HttpCode, HTTPMethod } from '~/common/http/http.js';
import { HTTPCode, HTTPMethod } from '~/common/http/http.js';
import { type Logger } from '~/common/logger/logger.js';

import { type AuthService } from './auth.service.js';
Expand Down Expand Up @@ -95,7 +95,7 @@ class AuthController extends BaseController {
): Promise<ApiHandlerResponse> {
return {
payload: await this.authService.signIn(options.body),
status: HttpCode.OK,
status: HTTPCode.OK,
};
}

Expand Down Expand Up @@ -144,7 +144,7 @@ class AuthController extends BaseController {
}>,
): Promise<ApiHandlerResponse> {
return {
status: HttpCode.CREATED,
status: HTTPCode.CREATED,
payload: await this.authService.signUp(options.body),
};
}
Expand Down
12 changes: 6 additions & 6 deletions backend/src/bundles/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
type UserSignInRequestDto,
type UserSignInResponseDto,
} from '~/bundles/users/users.js';
import { HttpCode, HttpError } from '~/common/http/http.js';
import { HTTPCode, HttpError } from '~/common/http/http.js';
import { cryptService, tokenService } from '~/common/services/services.js';

import { UserValidationMessage } from './enums/enums.js';
Expand All @@ -28,21 +28,21 @@ class AuthService {
if (!user) {
throw new HttpError({
message: UserValidationMessage.WRONG_CREDENTIALS,
status: HttpCode.BAD_REQUEST,
status: HTTPCode.BAD_REQUEST,
});
}

const { passwordHash } = user.toNewObject();

const isPwdCorrect = cryptService.compareSyncPassword(
const isPasswordCorrect = cryptService.compareSyncPassword(
password,
passwordHash,
);

if (!isPwdCorrect) {
if (!isPasswordCorrect) {
throw new HttpError({
message: UserValidationMessage.WRONG_CREDENTIALS,
status: HttpCode.BAD_REQUEST,
status: HTTPCode.BAD_REQUEST,
});
}

Expand All @@ -60,7 +60,7 @@ class AuthService {
if (emailExists) {
throw new HttpError({
message: UserValidationMessage.EMAIL_ALREADY_EXISTS,
status: HttpCode.BAD_REQUEST,
status: HTTPCode.BAD_REQUEST,
});
}
const user = await this.userService.create(userRequestDto);
Expand Down
6 changes: 3 additions & 3 deletions backend/src/bundles/avatar-videos/avatar-videos.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
type ApiHandlerResponse,
} from '~/common/controller/controller.js';
import { BaseController } from '~/common/controller/controller.js';
import { HttpCode, HTTPMethod } from '~/common/http/http.js';
import { HTTPCode, HTTPMethod } from '~/common/http/http.js';
import { type Logger } from '~/common/logger/logger.js';

import { type AvatarVideoService } from './avatar-videos.service.js';
Expand Down Expand Up @@ -77,6 +77,7 @@ class AvatarVideoController extends BaseController {
}>,
): Promise<ApiHandlerResponse> {
const userId = (options.user as UserGetCurrentResponseDto).id;

const videoRecord = await this.avatarVideoService.createVideo({
...options.body,
userId,
Expand All @@ -88,13 +89,12 @@ class AvatarVideoController extends BaseController {

await this.avatarVideoService.submitAvatarsConfigs(
avatarsConfigs,
userId,
videoRecord.id,
);

return {
payload: { status: ResponseStatus.SUBMITTED },
status: HttpCode.CREATED,
status: HTTPCode.CREATED,
};
}
}
Expand Down
134 changes: 95 additions & 39 deletions backend/src/bundles/avatar-videos/avatar-videos.service.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,54 @@
import { type VideoGetAllItemResponseDto } from 'shared';
import { HttpCode, HttpError } from 'shared';
import { HTTPCode, HttpError } from 'shared';
import { v4 as uuidv4 } from 'uuid';

import { type AvatarData } from '~/common/services/azure-ai/avatar-video/types/avatar-data.js';
import { type AzureAIService } from '~/common/services/azure-ai/azure-ai.service.js';
import { type FileService } from '~/common/services/file/file.service.js';
import { type RemotionService } from '~/common/services/remotion/remotion.service.js';
import { type RemotionAvatarScene } from '~/common/services/remotion/type/types.js';

import { type VideoService } from '../videos/video.service.js';
import { REQUEST_DELAY } from './constants/constnats.js';
import {
GenerateAvatarResponseStatus,
RenderVideoErrorMessage,
} from './enums/enums.js';
import { distributeScriptsToScenes, getFileName } from './helpers/helpers.js';
import { generatedAvatarToRemotionScene } from './helpers/generated-avatars-to-remotion-scenes.helper.js';
import {
distributeScriptsToScenes,
getFileName,
getTotalDuration,
} from './helpers/helpers.js';
import {
type Composition,
type GeneratedAvatarData,
type RenderAvatarVideoRequestDto,
} from './types/types.js';

type HandleRenderVideoArguments = {
videoRecordId: string;
avatars: {
id: string;
url: string;
}[];
type Constructor = {
azureAIService: AzureAIService;
fileService: FileService;
videoService: VideoService;
remotionService: RemotionService;
};

class AvatarVideoService {
private azureAIService: AzureAIService;
private fileService: FileService;
private videoService: VideoService;
private remotionService: RemotionService;

public constructor(
azureAIService: AzureAIService,
fileService: FileService,
videoService: VideoService,
) {
public constructor({
azureAIService,
fileService,
remotionService,
videoService,
}: Constructor) {
this.azureAIService = azureAIService;
this.fileService = fileService;
this.videoService = videoService;
this.remotionService = remotionService;
}

private async saveAvatarVideo(url: string, id: string): Promise<string> {
Expand Down Expand Up @@ -70,7 +80,6 @@ class AvatarVideoService {

public async submitAvatarsConfigs(
configs: AvatarData[],
userId: string,
recordId: string,
): Promise<string[]> {
try {
Expand All @@ -87,25 +96,24 @@ class AvatarVideoService {
return response.id;
});

this.checkAvatarsProcessing(ids, userId, recordId).catch(() => {
this.checkAvatarsProcessing(ids, recordId).catch(() => {
throw new HttpError({
message: RenderVideoErrorMessage.RENDER_ERROR,
status: HttpCode.BAD_REQUEST,
status: HTTPCode.BAD_REQUEST,
});
});

return ids;
} catch {
throw new HttpError({
message: RenderVideoErrorMessage.RENDER_ERROR,
status: HttpCode.BAD_REQUEST,
status: HTTPCode.BAD_REQUEST,
});
}
}

public async checkAvatarsProcessing(
ids: string[],
userId: string,
videoRecordId: string,
): Promise<void> {
try {
Expand All @@ -116,20 +124,18 @@ class AvatarVideoService {
);

await this.handleSuccessfulAvatarsGeneration({
avatars: response,
generatedAvatars: response,
videoRecordId,
});
} catch {
throw new HttpError({
message: RenderVideoErrorMessage.RENDER_ERROR,
status: HttpCode.BAD_REQUEST,
status: HTTPCode.BAD_REQUEST,
});
}
}

private checkAvatarStatus(
id: string,
): Promise<{ id: string; url: string }> {
private checkAvatarStatus(id: string): Promise<GeneratedAvatarData> {
return new Promise((resolve, reject) => {
const interval = setInterval(() => {
this.azureAIService
Expand All @@ -140,7 +146,12 @@ class AvatarVideoService {
GenerateAvatarResponseStatus.SUCCEEDED
) {
clearInterval(interval);
resolve({ id, url: response.outputs.result });
resolve({
id,
url: response.outputs.result,
durationInMilliseconds:
response.properties.durationInMilliseconds,
});
} else if (
response.status ===
GenerateAvatarResponseStatus.FAILED
Expand All @@ -149,7 +160,7 @@ class AvatarVideoService {
new HttpError({
message:
RenderVideoErrorMessage.RENDER_ERROR,
status: HttpCode.BAD_REQUEST,
status: HTTPCode.BAD_REQUEST,
}),
);
clearInterval(interval);
Expand All @@ -159,7 +170,7 @@ class AvatarVideoService {
reject(
new HttpError({
message: RenderVideoErrorMessage.RENDER_ERROR,
status: HttpCode.BAD_REQUEST,
status: HTTPCode.BAD_REQUEST,
}),
);
clearInterval(interval);
Expand All @@ -170,36 +181,81 @@ class AvatarVideoService {

private async handleSuccessfulAvatarsGeneration({
videoRecordId,
avatars,
}: HandleRenderVideoArguments): Promise<void> {
// TODO: REPLACE THIS LOGIC WITH RENDER VIDEO
// TODO: NOTIFY USER
const firstAvatarId = avatars[0]?.id;
const url = avatars[0]?.url;
generatedAvatars,
}: {
videoRecordId: string;
generatedAvatars: GeneratedAvatarData[];
}): Promise<void> {
const scenes = generatedAvatarToRemotionScene(generatedAvatars);
const scenesWithSavedAvatars = await this.saveGeneratedAvatar(scenes);

const renderId = await this.remotionService.renderVideo({
scenes: scenesWithSavedAvatars,
totalDurationInFrames: getTotalDuration(scenesWithSavedAvatars),
});

const url =
await this.remotionService.getRemotionRenderProgress(renderId);

if (!firstAvatarId || !url) {
await this.removeGeneratedAvatars(generatedAvatars);
await this.removeAvatarsFromBucket(generatedAvatars);

if (!url) {
return;
}
// TODO: NOTIFY USER
await this.updateVideoRecord(videoRecordId, url);
}

const savedUrl = await this.saveAvatarVideo(url, firstAvatarId);

private async updateVideoRecord(
videoRecordId: string,
videoUrl: string,
): Promise<void> {
const videoData = await this.videoService.update(videoRecordId, {
url: savedUrl,
url: videoUrl,
});

if (!videoData) {
throw new HttpError({
message: RenderVideoErrorMessage.NOT_SAVED,
status: HttpCode.BAD_REQUEST,
status: HTTPCode.BAD_REQUEST,
});
}
}

await Promise.all(
avatars.map((avatar) => {
private async removeGeneratedAvatars(
generatedAvatars: GeneratedAvatarData[],
): Promise<unknown> {
return Promise.all(
generatedAvatars.map((avatar) => {
return this.azureAIService.removeAvatarVideo(avatar.id);
}),
);
}

private async saveGeneratedAvatar(
generatedAvatars: RemotionAvatarScene[],
): Promise<RemotionAvatarScene[]> {
return Promise.all(
generatedAvatars.map(async (avatar) => {
return {
durationInFrames: avatar.durationInFrames,
id: avatar.id,
url: await this.saveAvatarVideo(avatar.url, avatar.id),
};
}),
);
}

private async removeAvatarsFromBucket(
generatedAvatars: GeneratedAvatarData[],
): Promise<unknown> {
return Promise.all(
generatedAvatars.map((avatar) => {
return this.fileService.deleteFile(getFileName(avatar.id));
}),
);
}
}

export { AvatarVideoService };
Loading

0 comments on commit 5273374

Please sign in to comment.