Skip to content

Commit

Permalink
OV-396: + merge
Browse files Browse the repository at this point in the history
  • Loading branch information
sergiy4 committed Sep 25, 2024
2 parents 464d893 + 64c3327 commit 7ce6f65
Show file tree
Hide file tree
Showing 42 changed files with 575 additions and 53 deletions.
1 change: 1 addition & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"pino": "9.3.2",
"pino-pretty": "10.3.1",
"shared": "*",
"socket.io": "4.7.5",
"swagger-jsdoc": "6.2.8",
"tiktoken": "1.0.16"
}
Expand Down
3 changes: 3 additions & 0 deletions backend/src/bundles/avatar-videos/avatar-videos.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { type VideoGetAllItemResponseDto, HTTPCode, HttpError } from 'shared';

import { AvatarVideoEvent } from '~/common/enums/enums.js';
import { type RemotionService } from '~/common/services/remotion/remotion.service.js';
import { socketEvent } from '~/common/socket/socket.js';

import { type VideoService } from '../videos/video.service.js';
import { RenderVideoErrorMessage } from './enums/enums.js';
Expand Down Expand Up @@ -129,6 +131,7 @@ class AvatarVideoService {
if (url) {
// TODO: NOTIFY USER
await this.videoService.update(videoRecordId, { url });
socketEvent.emitNotification(AvatarVideoEvent.RENDER_SUCCESS);
}

await this.scenesService.clearAvatars(compositionForRender.scenes);
Expand Down
1 change: 1 addition & 0 deletions backend/src/common/constants/constants.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { SOCKET_TRANSPORT_WEBSOCKETS } from './socket-trasnport.constants.js';
export { USER_PASSWORD_SALT_ROUNDS } from './user.constants.js';
export { WHITE_ROUTES } from './white-routes.constants.js';
3 changes: 3 additions & 0 deletions backend/src/common/constants/socket-trasnport.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const SOCKET_TRANSPORT_WEBSOCKETS = 'websocket';

export { SOCKET_TRANSPORT_WEBSOCKETS };
6 changes: 6 additions & 0 deletions backend/src/common/enums/avatar-video-event.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const AvatarVideoEvent = {
RENDER_SUCCESS: 'render:success',
RENDER_FAILED: 'render:failed',
} as const;

export { AvatarVideoEvent };
2 changes: 2 additions & 0 deletions backend/src/common/enums/enums.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export { AvatarVideoEvent } from './avatar-video-event.enum.js';
export { ParametersValidationMessage } from './parameters-validation-message.enum.js';
export { SocketEvent } from './socket-event.enum.js';
export { ApiPath, AppEnvironment, ServerErrorType } from 'shared';
6 changes: 6 additions & 0 deletions backend/src/common/enums/socket-event.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const SocketEvent = {
CONNECTION: 'connection',
DISCONNECT: 'disconnect',
} as const;

export { SocketEvent };
27 changes: 23 additions & 4 deletions backend/src/common/server-application/base-server-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,27 @@ import Fastify, {
type FastifyReply,
type FastifyRequest,
} from 'fastify';
import { type Socket, Server } from 'socket.io';

import { type Config } from '~/common/config/config.js';
import {
SOCKET_TRANSPORT_WEBSOCKETS,
WHITE_ROUTES,
} from '~/common/constants/constants.js';
import { type Database } from '~/common/database/database.js';
import { ServerErrorType } from '~/common/enums/enums.js';
import { ServerErrorType, SocketEvent } from '~/common/enums/enums.js';
import { type ValidationError } from '~/common/exceptions/exceptions.js';
import { HTTPCode, HttpError } from '~/common/http/http.js';
import { HTTPCode, HttpError, HTTPMethod } from '~/common/http/http.js';
import { type Logger } from '~/common/logger/logger.js';
import { authenticateJWT } from '~/common/plugins/plugins.js';
import { session } from '~/common/plugins/session/session.plugin.js';
import {
type ServerCommonErrorResponse,
type ServerValidationErrorResponse,
type ValidationSchema,
} from '~/common/types/types.js';

import { WHITE_ROUTES } from '../constants/constants.js';
import { authenticateJWT } from '../plugins/plugins.js';
import { initSocketConnection } from './socket-application.js';
import {
type ServerApp,
type ServerAppApi,
Expand All @@ -51,13 +56,23 @@ class BaseServerApp implements ServerApp {

private app: ReturnType<typeof Fastify>;

private io: Server;

public constructor({ config, logger, database, apis }: Constructor) {
this.config = config;
this.logger = logger;
this.database = database;
this.apis = apis;

this.app = Fastify();
this.io = new Server(this.app.server, {
// This is to ensure that it dosent fall back to long polling as it return a 404 if it does
transports: [SOCKET_TRANSPORT_WEBSOCKETS],
cors: {
origin: this.config.ENV.APP.ORIGIN,
methods: [HTTPMethod.GET, HTTPMethod.POST],
},
});
}

public addRoute(parameters: ServerAppRouteParameters): void {
Expand Down Expand Up @@ -241,6 +256,10 @@ class BaseServerApp implements ServerApp {

this.database.connect();

this.io.on(SocketEvent.CONNECTION, (socket: Socket) =>
initSocketConnection(this.io, socket),
);

await this.app
.listen({
port: this.config.ENV.APP.PORT,
Expand Down
12 changes: 12 additions & 0 deletions backend/src/common/server-application/socket-application.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { type Server, type Socket } from 'socket.io';

import { SocketEvent } from '../enums/enums.js';
import { socketEvent } from '../socket/socket.js';

const initSocketConnection = (io: Server, socket: Socket): void => {
socketEvent.initSocketConnection(io, socket);

socket.on(SocketEvent.DISCONNECT, () => {});
};

export { initSocketConnection };
31 changes: 31 additions & 0 deletions backend/src/common/socket/scoket-event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import EventTarget from 'node:events';

import { type Server, type Socket } from 'socket.io';

class SocketEvent extends EventTarget {
private io: Server | null = null;
private socket: Socket | null = null;

public initSocketConnection(io: Server, socket: Socket): void {
this.io = io;
this.socket = socket;
}

public getIo(): Server | null {
return this.io;
}

public getSocket(): Socket | null {
return this.socket;
}

public emitNotification(event: string): void {
if (this.socket) {
this.socket.emit(event);
}
}
}

const socketEvent = new SocketEvent();

export { socketEvent };
1 change: 1 addition & 0 deletions backend/src/common/socket/socket.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { socketEvent } from './scoket-event.js';
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"react-router-dom": "6.26.0",
"remotion": "4.0.201",
"shared": "*",
"socket.io-client": "4.7.5",
"zod-formik-adapter": "1.3.0"
},
"overrides": {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/bundles/common/constants/constants.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { FPS } from './fps.constant.js';
export { SOCKET_TRANSPORT_WEBSOCKETS } from './socket-trasnport.constants.js';
export { EMPTY_VALUE } from 'shared';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const SOCKET_TRANSPORT_WEBSOCKETS = 'websocket';

export { SOCKET_TRANSPORT_WEBSOCKETS };
14 changes: 14 additions & 0 deletions frontend/src/bundles/common/context/socket.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';
import { type Socket, io } from 'socket.io-client';

import { SOCKET_TRANSPORT_WEBSOCKETS } from '~/bundles/common/constants/constants.js';

const serverUrl = import.meta.env['VITE_APP_PROXY_SERVER_URL'];

const socket = io(serverUrl, {
// This is to ensure that it dosent fall back to long polling as it return a 404 if it does
transports: [SOCKET_TRANSPORT_WEBSOCKETS],
});
const SocketContext = React.createContext<Socket>(socket);

export { socket, SocketContext };
6 changes: 6 additions & 0 deletions frontend/src/bundles/common/enums/avatar-video-event.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const AvatarVideoEvent = {
RENDER_SUCCESS: 'render:success',
RENDER_FAILED: 'render:failed',
} as const;

export { AvatarVideoEvent };
1 change: 1 addition & 0 deletions frontend/src/bundles/common/enums/enums.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { AppRoute } from './app-route.enum.js';
export { AvatarVideoEvent } from './avatar-video-event.enum.js';
export { DataStatus } from './data-status.enum.js';
export { DOMEvent } from './dom-event.enum.js';
export { VideoPreview } from './video-preview.enum.js';
Expand Down
1 change: 1 addition & 0 deletions frontend/src/bundles/common/hooks/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export { useAppSelector } from './use-app-selector/use-app-selector.hook.js';
export { useFormField } from './use-form-field/use-form-field.hook.js';
export {
useCallback,
useContext,
useEffect,
useLayoutEffect,
useMemo,
Expand Down
55 changes: 51 additions & 4 deletions frontend/src/bundles/home/components/main-content/main-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,23 @@ import {
Overlay,
} from '~/bundles/common/components/components.js';
import { useCollapse } from '~/bundles/common/components/sidebar/hooks/use-collapse.hook.js';
import { DataStatus } from '~/bundles/common/enums/enums.js';
import { SocketContext } from '~/bundles/common/context/socket.js';
import { AvatarVideoEvent, DataStatus } from '~/bundles/common/enums/enums.js';
import {
useAppDispatch,
useAppSelector,
useContext,
useEffect,
} from '~/bundles/common/hooks/hooks.js';
import { notificationService } from '~/bundles/common/services/services.js';
import {
VIDEO_RENDER_FAILED_NOTIFICATION_ID,
VIDEO_RENDER_SUCCESS_NOTIFICATION_ID,
} from '~/bundles/home/constants/constants.js';
import {
NotificationMessage,
NotificationTitle,
} from '~/bundles/home/enums/enums.js';
import { VideoGallery } from '~/bundles/home/enums/video-gallery.js';
import { actions as homeActions } from '~/bundles/home/store/home.js';

Expand All @@ -19,14 +30,50 @@ import styles from './styles.module.css';
const MainContent: React.FC = () => {
const dispatch = useAppDispatch();
const { isCollapsed } = useCollapse();
const socket = useContext(SocketContext);

const { videos, dataStatus } = useAppSelector(({ home }) => home);

// TODO: filter videos to get recent videos

useEffect(() => {
void dispatch(homeActions.loadUserVideos());
}, [dispatch]);
const handleLoadUserVideos = (): void => {
void dispatch(homeActions.loadUserVideos());
};

const handleRenderedVideoSuccess = (): void => {
notificationService.success({
id: VIDEO_RENDER_SUCCESS_NOTIFICATION_ID,
title: NotificationTitle.VIDEO_RENDER_SUCCESS,
message: NotificationMessage.VIDEO_RENDER_SUCCESS,
});
handleLoadUserVideos();
};

const handleRenderedVideoFailed = (): void => {
socket.on(AvatarVideoEvent.RENDER_FAILED, () => {
notificationService.error({
id: VIDEO_RENDER_FAILED_NOTIFICATION_ID,
title: NotificationTitle.VIDEO_RENDER_FAILED,
message: NotificationMessage.VIDEO_RENDER_FAILED,
});
});
};

handleLoadUserVideos();
socket.on(AvatarVideoEvent.RENDER_SUCCESS, handleRenderedVideoSuccess);
socket.on(AvatarVideoEvent.RENDER_FAILED, handleRenderedVideoFailed);

return () => {
socket.off(
AvatarVideoEvent.RENDER_SUCCESS,
handleRenderedVideoSuccess,
);
socket.off(
AvatarVideoEvent.RENDER_FAILED,
handleRenderedVideoFailed,
);
};
}, [dispatch, socket]);

return (
<Box
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/bundles/home/constants/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { VIDEO_RENDER_FAILED_NOTIFICATION_ID } from './video-render-failed-notification-id.constant.js';
export { VIDEO_RENDER_SUCCESS_NOTIFICATION_ID } from './video-render-success-notification-id.constant.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const VIDEO_RENDER_FAILED_NOTIFICATION_ID = 'video-render-failed';

export { VIDEO_RENDER_FAILED_NOTIFICATION_ID };
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const VIDEO_RENDER_SUCCESS_NOTIFICATION_ID = 'video-render-success';

export { VIDEO_RENDER_SUCCESS_NOTIFICATION_ID };
2 changes: 2 additions & 0 deletions frontend/src/bundles/home/enums/enums.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { NotificationMessage } from './notification-message.enum.js';
export { NotificationTitle } from './notification-title.enum.js';
6 changes: 6 additions & 0 deletions frontend/src/bundles/home/enums/notification-message.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const NotificationMessage = {
VIDEO_RENDER_SUCCESS: 'Video rendered successfully',
VIDEO_RENDER_FAILED: 'Video render failed',
} as const;

export { NotificationMessage };
6 changes: 6 additions & 0 deletions frontend/src/bundles/home/enums/notification-title.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const NotificationTitle = {
VIDEO_RENDER_SUCCESS: 'Video rendered',
VIDEO_RENDER_FAILED: 'Video render failed',
} as const;

export { NotificationTitle };
17 changes: 15 additions & 2 deletions frontend/src/bundles/studio/components/timeline/item/item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { type Span } from 'dnd-timeline';

import { Box, Flex } from '~/bundles/common/components/components.js';
import { useAppSelector } from '~/bundles/common/hooks/hooks.js';
import { RowNames } from '~/bundles/studio/enums/enums.js';
import { useTimelineItem } from '~/bundles/studio/hooks/hooks.js';
import { type RowType } from '~/bundles/studio/types/types.js';

Expand All @@ -13,9 +14,19 @@ type Properties = {
span: Span;
children: React.ReactNode;
onClick?: React.MouseEventHandler<HTMLElement>;
onResizeStart?: () => void;
onResizeEnd?: () => void;
};

const Item: React.FC<Properties> = ({ id, type, span, children, onClick }) => {
const Item: React.FC<Properties> = ({
id,
type,
span,
children,
onClick,
onResizeStart = (): void => {},
onResizeEnd = (): void => {},
}) => {
const selectedItem = useAppSelector(({ studio }) => studio.ui.selectedItem);

const {
Expand All @@ -29,12 +40,14 @@ const Item: React.FC<Properties> = ({ id, type, span, children, onClick }) => {
id,
span,
data: { type },
onResizeEnd,
onResizeStart,
});

return (
<Box
ref={setNodeRef}
{...listeners}
{...(type !== RowNames.BUTTON && listeners)}
{...attributes}
style={itemStyle}
zIndex={isDragging ? '100' : 'auto'}
Expand Down
Loading

0 comments on commit 7ce6f65

Please sign in to comment.