Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
JonnyBurger authored Sep 14, 2023
0 parents commit 7269c75
Show file tree
Hide file tree
Showing 42 changed files with 1,690 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
REMOTION_AWS_ACCESS_KEY_ID=""
REMOTION_AWS_SECRET_ACCESS_KEY=""
10 changes: 10 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "next",
"plugins": ["@remotion"],
"overrides": [
{
"files": ["remotion/**/*.{ts,tsx}"],
"extends": ["plugin:@remotion/recommended"]
}
]
}
38 changes: 38 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
out/
.env
76 changes: 76 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<img src="https://github.com/remotion-dev/template-next/assets/1629785/9092db5f-7c0c-4d38-97c4-5f5a61f5cc098" />
<br/>
<br/>

This is a Next.js template for building programmatic video apps, with [`@remotion/player`](https://remotion.dev/player) and [`@remotion/lambda`](https://remotion.dev/lambda) built in.

This template uses the Next.js App directory. There is a [Pages directory version](https://github.com/remotion-dev/template-next-pages-dir) of this template available.

<img src="https://github.com/remotion-dev/template-next/assets/1629785/c9c2e5ca-2637-4ec8-8e40-a8feb5740d88" />

## Getting Started

[Use this template](https://github.com/new?template_name=template-next-app-dir&template_owner=remotion-dev) to clone it into your GitHub account. Run

```
npm i
```

afterwards. Alternatively, use this command to scaffold a project:

```
npx create-video@latest --next
```

## Commands

Start the Next.js dev server:

```
npm run dev
```

Open the Remotion Studio:

```
npm run remotion
```

The following script will set up your Remotion Bundle and Lambda function on AWS:

```
node deploy.mjs
```

You should run this script after:

- changing the video template
- changing `config.mjs`
- upgrading Remotion to a newer version

## Set up rendering on AWS Lambda

This template supports rendering the videos via [Remotion Lambda](https://remotion.dev/lambda).

1. Copy the `.env.example` file to `.env` and fill in the values.
Complete the [Lambda setup guide](https://www.remotion.dev/docs/lambda/setup) to get your AWS credentials.

1. Edit the `config.mjs` file to your desired Lambda settings.

1. Run `node deploy.mjs` to deploy your Lambda function and Remotion Bundle.

## Docs

Get started with Remotion by reading the [fundamentals page](https://www.remotion.dev/docs/the-fundamentals).

## Help

We provide help on our [Discord server](https://remotion.dev/discord).

## Issues

Found an issue with Remotion? [File an issue here](https://remotion.dev/issue).

## License

Note that for some entities a company license is needed. [Read the terms here](https://github.com/remotion-dev/remotion/blob/main/LICENSE.md).
48 changes: 48 additions & 0 deletions app/api/lambda/progress/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
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) => {
if (req.method !== "POST") {
throw new Error("Only POST requests are allowed");
}

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),
};
}
);
54 changes: 54 additions & 0 deletions app/api/lambda/render/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
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 (req.method !== "POST") {
throw new Error("Only POST requests are allowed");
}

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;
}
);
Binary file added app/favicon.ico
Binary file not shown.
22 changes: 22 additions & 0 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import "../styles/global.css";
import { Metadata } from "next";

export const metadata: Metadata = {
title: "Remotion and Next.js",
description: "Remotion and Next.js",
viewport: "width=device-width, initial-scale=1, maximum-scale=1",
};

export default function RootLayout({
// Layouts must accept a children prop.
// This will be populated with nested layouts or pages
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
79 changes: 79 additions & 0 deletions app/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;
11 changes: 11 additions & 0 deletions 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 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);
Loading

0 comments on commit 7269c75

Please sign in to comment.