Skip to content

Commit

Permalink
feat: add remotion
Browse files Browse the repository at this point in the history
  • Loading branch information
cs50victor committed Feb 21, 2024
1 parent b3eb1b8 commit 5b4e8f8
Show file tree
Hide file tree
Showing 37 changed files with 4,606 additions and 301 deletions.
3,492 changes: 3,194 additions & 298 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

44 changes: 44 additions & 0 deletions www/app/api/lambda/progress/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {
speculateFunctionName,
AwsRegion,
getRenderProgress,
} from "@remotion/lambda/client";
import { DISK, RAM, REGION, TIMEOUT } from "~/config.mjs";
import { executeApi } from "~/helpers/api-response";
import { ProgressRequest, ProgressResponse } from "~/types/schema";

export const POST = executeApi<ProgressResponse, typeof ProgressRequest>(
ProgressRequest,
async (req, body) => {
const renderProgress = await getRenderProgress({
bucketName: body.bucketName,
functionName: speculateFunctionName({
diskSizeInMb: DISK,
memorySizeInMb: RAM,
timeoutInSeconds: TIMEOUT,
}),
region: REGION as AwsRegion,
renderId: body.id,
});

if (renderProgress.fatalErrorEncountered) {
return {
type: "error",
message: renderProgress.errors[0].message,
};
}

if (renderProgress.done) {
return {
type: "done",
url: renderProgress.outputFile as string,
size: renderProgress.outputSizeInBytes as number,
};
}

return {
type: "progress",
progress: Math.max(0.03, renderProgress.overallProgress),
};
}
);
50 changes: 50 additions & 0 deletions www/app/api/lambda/render/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { AwsRegion, RenderMediaOnLambdaOutput } from "@remotion/lambda/client";
import {
renderMediaOnLambda,
speculateFunctionName,
} from "@remotion/lambda/client";
import { DISK, RAM, REGION, SITE_NAME, TIMEOUT } from "~/config.mjs";
import { executeApi } from "~/helpers/api-response";
import { RenderRequest } from "~/types/schema";

export const POST = executeApi<RenderMediaOnLambdaOutput, typeof RenderRequest>(
RenderRequest,
async (req, body) => {
if (
!process.env.AWS_ACCESS_KEY_ID &&
!process.env.REMOTION_AWS_ACCESS_KEY_ID
) {
throw new TypeError(
"Set up Remotion Lambda to render videos. See the README.md for how to do so."
);
}
if (
!process.env.AWS_SECRET_ACCESS_KEY &&
!process.env.REMOTION_AWS_SECRET_ACCESS_KEY
) {
throw new TypeError(
"The environment variable REMOTION_AWS_SECRET_ACCESS_KEY is missing. Add it to your .env file."
);
}

const result = await renderMediaOnLambda({
codec: "h264",
functionName: speculateFunctionName({
diskSizeInMb: DISK,
memorySizeInMb: RAM,
timeoutInSeconds: TIMEOUT,
}),
region: REGION as AwsRegion,
serveUrl: SITE_NAME,
composition: body.id,
inputProps: body.inputProps,
framesPerLambda: 10,
downloadBehavior: {
type: "download",
fileName: "video.mp4",
},
});

return result;
}
);
79 changes: 79 additions & 0 deletions www/app/editor/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"use client";

import { Player } from "@remotion/player";
import type { NextPage } from "next";
import React, { useMemo, useState } from "react";
import { Main } from "~/remotion/MyComp/Main";
import {
CompositionProps,
defaultMyCompProps,
DURATION_IN_FRAMES,
VIDEO_FPS,
VIDEO_HEIGHT,
VIDEO_WIDTH,
} from "~/types/constants";
import { z } from "zod";
import { RenderControls } from "~/components/RenderControls";
import { Tips } from "~/components/Tips/Tips";
import { Spacing } from "~/components/Spacing";

const container: React.CSSProperties = {
maxWidth: 768,
margin: "auto",
marginBottom: 20,
};

const outer: React.CSSProperties = {
borderRadius: "var(--geist-border-radius)",
overflow: "hidden",
boxShadow: "0 0 200px rgba(0, 0, 0, 0.15)",
marginBottom: 40,
marginTop: 60,
};

const player: React.CSSProperties = {
width: "100%",
};

const Home: NextPage = () => {
const [text, setText] = useState<string>(defaultMyCompProps.title);

const inputProps: z.infer<typeof CompositionProps> = useMemo(() => {
return {
title: text,
};
}, [text]);

return (
<div>
<div style={container}>
<div className="cinematics" style={outer}>
<Player
component={Main}
inputProps={inputProps}
durationInFrames={DURATION_IN_FRAMES}
fps={VIDEO_FPS}
compositionHeight={VIDEO_HEIGHT}
compositionWidth={VIDEO_WIDTH}
style={player}
controls
autoPlay
loop
/>
</div>
<RenderControls
text={text}
setText={setText}
inputProps={inputProps}
></RenderControls>
<Spacing></Spacing>
<Spacing></Spacing>
<Spacing></Spacing>
<Spacing></Spacing>
<Tips></Tips>
</div>
</div>
);
};

export default Home;
2 changes: 1 addition & 1 deletion www/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const metadata: Metadata = {
metadataBase: new URL(`https://${process.env.VERCEL_URL}`),
title: {
default: 'New Media',
template: '%s - Media',
template: '%s - New Media',
},
};

Expand Down
11 changes: 11 additions & 0 deletions www/components/AlignEnd.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from "react";

const container: React.CSSProperties = {
alignSelf: "flex-end",
};

export const AlignEnd: React.FC<{
children: React.ReactNode;
}> = ({ children }) => {
return <div style={container}>{children}</div>;
};
37 changes: 37 additions & 0 deletions www/components/Button/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React, { forwardRef } from "react";
import { Spacing } from "../Spacing";
import { Spinner } from "../Spinner/Spinner";
import styles from "./styles.module.css";

const ButtonForward: React.ForwardRefRenderFunction<
HTMLButtonElement,
{
onClick?: () => void;
disabled?: boolean;
children: React.ReactNode;
loading?: boolean;
secondary?: boolean;
}
> = ({ onClick, disabled, children, loading, secondary }, ref) => {
return (
<button
ref={ref}
className={[
styles.button,
secondary ? styles.secondarybutton : undefined,
].join(" ")}
onClick={onClick}
disabled={disabled}
>
{loading && (
<>
<Spinner size={20}></Spinner>
<Spacing></Spacing>
</>
)}
{children}
</button>
);
};

export const Button = forwardRef(ButtonForward);
37 changes: 37 additions & 0 deletions www/components/Button/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.button {
border: 1px solid var(--foreground);
border-radius: var(--geist-border-radius);
background-color: var(--foreground);
color: var(--background);
padding-left: var(--geist-half-pad);
padding-right: var(--geist-half-pad);
height: 40px;
font-family: var(--geist-font);
font-weight: 500;
transition: all 0.15s ease;
display: inline-flex;
flex-direction: row;
align-items: center;
appearance: none;
font-size: 14px;
}

.secondarybutton {
background-color: var(--background);
color: var(--foreground);
border-color: var(--unfocused-border-color);
}

.button:hover {
background-color: var(--background);
cursor: pointer;
color: var(--foreground);
border-color: var(--focused-border-color);
}

.button:disabled {
background-color: var(--button-disabled-color);
color: var(--disabled-text-color);
border-color: var(--unfocused-border-color);
cursor: not-allowed
}
16 changes: 16 additions & 0 deletions www/components/Container.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from "react";

const inputContainer: React.CSSProperties = {
border: "1px solid var(--unfocused-border-color)",
padding: "var(--geist-pad)",
borderRadius: "var(--geist-border-radius)",
backgroundColor: "var(--background)",
display: "flex",
flexDirection: "column",
};

export const InputContainer: React.FC<{
children: React.ReactNode;
}> = ({ children }) => {
return <div style={inputContainer}>{children}</div>;
};
69 changes: 69 additions & 0 deletions www/components/DownloadButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React from "react";
import { State } from "../helpers/use-rendering";
import { Button } from "./Button/Button";
import { Spacing } from "./Spacing";

const light: React.CSSProperties = {
opacity: 0.6,
};

const link: React.CSSProperties = {
textDecoration: "none",
};

const row: React.CSSProperties = {
display: "flex",
flexDirection: "row",
};

const Megabytes: React.FC<{
sizeInBytes: number;
}> = ({ sizeInBytes }) => {
const megabytes = Intl.NumberFormat("en", {
notation: "compact",
style: "unit",
unit: "byte",
unitDisplay: "narrow",
}).format(sizeInBytes);
return <span style={light}>{megabytes}</span>;
};

export const DownloadButton: React.FC<{
state: State;
undo: () => void;
}> = ({ state, undo }) => {
if (state.status === "rendering") {
return <Button disabled>Download video</Button>;
}

if (state.status !== "done") {
throw new Error("Download button should not be rendered when not done");
}

return (
<div style={row}>
<Button secondary onClick={undo}>
<UndoIcon></UndoIcon>
</Button>
<Spacing></Spacing>
<a style={link} href={state.url}>
<Button>
Download video
<Spacing></Spacing>
<Megabytes sizeInBytes={state.size}></Megabytes>
</Button>
</a>
</div>
);
};

const UndoIcon: React.FC = () => {
return (
<svg height="1em" viewBox="0 0 512 512">
<path
fill="var(--foreground)"
d="M48.5 224H40c-13.3 0-24-10.7-24-24V72c0-9.7 5.8-18.5 14.8-22.2s19.3-1.7 26.2 5.2L98.6 96.6c87.6-86.5 228.7-86.2 315.8 1c87.5 87.5 87.5 229.3 0 316.8s-229.3 87.5-316.8 0c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0c62.5 62.5 163.8 62.5 226.3 0s62.5-163.8 0-226.3c-62.2-62.2-162.7-62.5-225.3-1L185 183c6.9 6.9 8.9 17.2 5.2 26.2s-12.5 14.8-22.2 14.8H48.5z"
/>
</svg>
);
};
Loading

0 comments on commit 5b4e8f8

Please sign in to comment.