Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/next' into task/OV-423-generate-…
Browse files Browse the repository at this point in the history
…preview-with-bg
  • Loading branch information
Sanchousina committed Sep 26, 2024
2 parents 501b067 + c077a8e commit 08de0b0
Show file tree
Hide file tree
Showing 54 changed files with 1,216 additions and 240 deletions.
19 changes: 13 additions & 6 deletions backend/src/bundles/videos/video.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { VideoEntity } from '~/bundles/videos/video.entity.js';
import { type VideoRepository } from '~/bundles/videos/video.repository.js';
import { HTTPCode, HttpError } from '~/common/http/http.js';
import { type FileService } from '~/common/services/file/file.service.js';
import { type ImageService } from '~/common/services/image/image.service.js';
import { type RemotionService } from '~/common/services/remotion/remotion.service.js';
import { type Service } from '~/common/types/types.js';

import { VideoValidationMessage } from './enums/enums.js';
Expand All @@ -16,16 +16,16 @@ import {

class VideoService implements Service {
private videoRepository: VideoRepository;
private fileService: FileService;
private remotionService: RemotionService;
private imageService: ImageService;

public constructor(
videoRepository: VideoRepository,
fileService: FileService,
remotionService: RemotionService,
imageService: ImageService,
) {
this.videoRepository = videoRepository;
this.fileService = fileService;
this.remotionService = remotionService;
this.imageService = imageService;
}

Expand Down Expand Up @@ -94,9 +94,16 @@ class VideoService implements Service {
}

public async delete(id: string): Promise<boolean> {
const { name } = await this.findById(id);
const { url } = await this.findById(id);

await this.fileService.deleteFile(name);
if (url) {
const renderIdMatch = url.match(/renders\/([^/]+)/);
const renderId = renderIdMatch?.[1];

if (renderId) {
await this.remotionService.deleteRenderedVideo(renderId);
}
}

const isVideoDeleted = await this.videoRepository.delete(id);

Expand Down
4 changes: 2 additions & 2 deletions backend/src/bundles/videos/videos.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { logger } from '~/common/logger/logger.js';
import { fileService, imageService } from '~/common/services/services.js';
import { imageService, remotionService } from '~/common/services/services.js';

import { VideoController } from './video.controller.js';
import { VideoModel } from './video.model.js';
Expand All @@ -9,7 +9,7 @@ import { VideoService } from './video.service.js';
const videoRepository = new VideoRepository(VideoModel, imageService);
const videoService = new VideoService(
videoRepository,
fileService,
remotionService,
imageService,
);
const videoController = new VideoController(logger, videoService);
Expand Down
9 changes: 9 additions & 0 deletions backend/src/common/services/remotion/remotion.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
type AwsRegion,
deleteRender,
getRenderProgress,
renderMediaOnLambda,
} from '@remotion/lambda/client';
Expand Down Expand Up @@ -27,6 +28,14 @@ class RemotionService {
this.config.ENV.AWS.CLOUDFRONT.DOMAIN_ID_FOR_RENDERED_VIDEO;
}

public async deleteRenderedVideo(renderId: string): Promise<void> {
await deleteRender({
bucketName: this.config.ENV.REMOTION.BUCKET_NAME,
region: this.config.ENV.AWS.S3.REGION as AwsRegion,
renderId,
});
}

public async renderVideo(
inputProperties: InputProperties,
): Promise<string> {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/bundles/chat/helpers/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { sanitizeJsonString } from './sanitize-json-string.js';
6 changes: 6 additions & 0 deletions frontend/src/bundles/chat/helpers/sanitize-json-string.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const sanitizeJsonString = (input: string): string => {
const match = input.match(/\[.*]/s);
return match ? match[0].trim() : input.trim();
};

export { sanitizeJsonString };
44 changes: 42 additions & 2 deletions frontend/src/bundles/chat/store/slice.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,67 @@
import { createSlice } from '@reduxjs/toolkit';

import { MessageSender } from '~/bundles/chat/enums/enums.js';
import { sanitizeJsonString } from '~/bundles/chat/helpers/helpers.js';
import {
type GenerateTextRequestDto,
type Message,
} from '~/bundles/chat/types/types.js';
import {
EMPTY_VALUE,
LAST_ELEMENT_INDEX,
} from '~/bundles/common/constants/constants.js';
import { DataStatus } from '~/bundles/common/enums/enums.js';
import { type ValueOf } from '~/bundles/common/types/types.js';
import {
type ValueOf,
type VideoScript,
} from '~/bundles/common/types/types.js';

import { deleteChat, sendMessage } from './actions.js';

type State = {
messages: Message[];
videoScripts: VideoScript[];
videoScriptErrorMessage: string;
dataStatus: ValueOf<typeof DataStatus>;
};

const initialState: State = {
messages: [],
videoScripts: [],
videoScriptErrorMessage: '',
dataStatus: DataStatus.IDLE,
};

const { reducer, actions, name } = createSlice({
initialState,
name: 'chat',
reducers: {},
reducers: {
generateVideoScript(state) {
const messages = state.messages.filter(
(message) => message.sender === MessageSender.AI,
);

if (!messages || messages.length === EMPTY_VALUE) {
return;
}

const lastMessage = messages.at(LAST_ELEMENT_INDEX);
if (!lastMessage) {
return;
}

try {
const sanitizedJson = sanitizeJsonString(lastMessage.text);
const videoScripts: VideoScript[] = JSON.parse(sanitizedJson);
state.videoScripts = videoScripts;
state.videoScriptErrorMessage = '';
} catch {
state.videoScripts = [];
state.videoScriptErrorMessage =
'There was an error Generating the Script, please Re-Generate';
}
},
},
extraReducers(builder) {
builder.addCase(sendMessage.pending, (state) => {
state.dataStatus = DataStatus.PENDING;
Expand Down Expand Up @@ -60,6 +98,8 @@ const { reducer, actions, name } = createSlice({
});
builder.addCase(deleteChat.fulfilled, (state) => {
state.messages = [];
state.videoScripts = [];
state.videoScriptErrorMessage = '';
state.dataStatus = DataStatus.FULFILLED;
});
builder.addCase(deleteChat.rejected, (state) => {
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/bundles/common/components/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export { ProtectedRoute } from './protected-route/protected-route.js';
export { RouterProvider } from './router-provider/router-provider.js';
export { Select } from './select/select.js';
export { Sidebar } from './sidebar/sidebar.js';
export { Stepper } from './stepper/stepper.js';
export { Textarea } from './textarea/textarea.js';
export { UploadVideo } from './upload-video/upload-video.js';
export { VideoModal } from './video-modal/video-modal.js';
Expand Down Expand Up @@ -51,6 +52,7 @@ export {
Input as LibraryInput,
Link as LibraryLink,
Modal as LibraryModal,
Stepper as LibraryStepper,
ListItem,
Menu,
MenuButton,
Expand All @@ -61,6 +63,7 @@ export {
ModalCloseButton,
ModalContent,
ModalOverlay,
Progress,
SimpleGrid,
Slider,
SliderFilledTrack,
Expand All @@ -69,6 +72,9 @@ export {
Spacer,
Spinner,
Stack,
Step,
StepIndicator,
StepStatus,
Tab,
TabList,
TabPanel,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/bundles/common/components/header/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const Header: React.FC<Properties> = ({ left, center, right }) => {
</Box>
)}
<Box className={styles['center']}>{center}</Box>
<Box className={styles['right']}>{right}</Box>
{right && <Box className={styles['right']}>{right}</Box>};
</Flex>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { StepStatus } from './step-status/step-status.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { type ValueOf } from 'shared';

import { Box, Icon } from '~/bundles/common/components/components.js';
import { StepStatus as StepStatusEnum } from '~/bundles/common/components/stepper/enums/enums.js';
import { IconName } from '~/bundles/common/icons/icons.js';

import styles from './styles.module.css';

type Properties = {
step: ValueOf<typeof StepStatusEnum>;
};

const StepStatus: React.FC<Properties> = ({ step }) => {
switch (step) {
case StepStatusEnum.COMPLETE: {
return (
<Box className={styles['complete']}>
<Icon as={IconName.CHECK_CIRCLE} boxSize={3} />
</Box>
);
}

case StepStatusEnum.ACTIVE: {
return (
<Box className={styles['active']}>
<Icon as={IconName.CIRCLE} boxSize={3} />
</Box>
);
}

case StepStatusEnum.INCOMPLETE: {
return (
<Box className={styles['incomplete']}>
<Icon as={IconName.CIRCLE} boxSize={3} />
</Box>
);
}
}
};

export { StepStatus };
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.complete {
background-color: var(--chakra-colors-background-900);
display: inline-flex;
}

.active {
display: inline-flex;
justify-content: center;
align-items: center;
padding: 4px;
color: white;
border: 2px solid;
border-color: var(--chakra-colors-background-600);
border-radius: 50%;
background-color: var(--chakra-colors-background-900);
}

.incomplete {
color: var(--chakra-colors-background-600);
display: inline-flex;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { StepStatus } from './step-status.enum.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const StepStatus = {
COMPLETE: 'complete',
ACTIVE: 'active',
INCOMPLETE: 'incomplete',
} as const;

export { StepStatus };
91 changes: 91 additions & 0 deletions frontend/src/bundles/common/components/stepper/stepper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import {
Box,
Header,
Icon,
IconButton,
LibraryStepper,
Progress,
Step,
StepIndicator,
StepStatus,
Text,
} from '~/bundles/common/components/components.js';
import { IconName } from '~/bundles/common/icons/icons.js';

import { StepStatus as StepIcon } from './components/components.js';
import { StepStatus as StepStatusEnum } from './enums/enums.js';
import styles from './styles.module.css';

type Properties = {
steps: string[];
currentStep: string;
};

const Stepper: React.FC<Properties> = ({ steps, currentStep }) => {
const activeStepIndex = steps.indexOf(currentStep);
const progressPercent = (activeStepIndex / (steps.length - 1)) * 100;

const backButton = (
<IconButton
variant="ghostIcon"
aria-label="back"
icon={<Icon as={IconName.ARROW_BACK} boxSize={4} />}
/>
);

const stepProgressBar = (
<Box className={styles['stepperWrapper']}>
<Box className={styles['innerStepper']}>
<LibraryStepper size="sm" index={activeStepIndex} gap="0">
{steps.map((step, index) => (
<Step key={index}>
<StepIndicator
bg="white"
height="12px"
width="12px"
>
<StepStatus
complete={
<StepIcon
step={StepStatusEnum.COMPLETE}
/>
}
active={
<StepIcon
step={StepStatusEnum.ACTIVE}
/>
}
incomplete={
<StepIcon
step={StepStatusEnum.INCOMPLETE}
/>
}
/>
<Text
variant="body1"
color={
index <= activeStepIndex
? 'white'
: 'background.600'
}
position="absolute"
top="20px"
>
{step}
</Text>
</StepIndicator>
</Step>
))}
</LibraryStepper>
<Progress
variant="stepper"
value={progressPercent}
className={styles['progressBar']}
/>
</Box>
</Box>
);
return <Header left={backButton} center={stepProgressBar} />;
};

export { Stepper };
Loading

0 comments on commit 08de0b0

Please sign in to comment.