├── app
├── favicon.ico
├── fonts
│ ├── GeistVF.woff
│ └── GeistMonoVF.woff
├── _context
│ ├── UserDetailContext.jsx
│ └── VideoDataContext.jsx
├── api
│ ├── get-video-script
│ │ └── route.jsx
│ ├── generate-caption
│ │ └── route.jsx
│ ├── generate-image
│ │ └── route.jsx
│ └── generate-audio-file
│ │ └── route.jsx
├── dashboard
│ ├── _components
│ │ ├── EmptyState.jsx
│ │ ├── Header.jsx
│ │ ├── SideNav.jsx
│ │ ├── VideoList.jsx
│ │ ├── PlayerDialog.jsx
│ │ └── RemotionVideo.jsx
│ ├── create-new
│ │ ├── _components
│ │ │ ├── Loading.jsx
│ │ │ ├── SelectDuration.jsx
│ │ │ ├── SelectTopic.jsx
│ │ │ └── SelectStyle.jsx
│ │ └── page.jsx
│ ├── layout.jsx
│ └── page.jsx
├── layout.js
├── provider.js
├── globals.css
├── (auth)
│ ├── sign-in
│ │ └── [[...sign-in]]
│ │ │ └── page.jsx
│ └── sign-up
│ │ └── [[...sign-up]]
│ │ └── page.jsx
└── page.js
├── public
├── logo.png
├── soon.gif
├── star.png
├── comic.png
├── login.png
├── cartoon.png
├── cyberpunk.png
├── realistic.png
├── watercolor.png
├── vercel.svg
├── window.svg
├── file.svg
├── globe.svg
├── next.svg
└── devpost.svg
├── jsconfig.json
├── screenshots
├── image (1).png
├── image (2).png
├── image (3).png
├── image (4).png
├── image (5).png
├── image (6).png
├── image (7).png
├── image (8).png
├── image (9).png
├── image (10).png
├── image (11).png
└── image (12).png
├── remotion
├── index.jsx
└── Root.jsx
├── lib
└── utils.js
├── postcss.config.mjs
├── configs
├── db.js
├── schema.js
├── FireBaseConfig.jsx
└── aimodel.js
├── next.config.mjs
├── drizzle.config.js
├── components.json
├── middleware.js
├── .gitignore
├── components
└── ui
│ ├── textarea.jsx
│ ├── sonner.jsx
│ ├── button.jsx
│ ├── dialog.jsx
│ ├── alert-dialog.jsx
│ └── select.jsx
├── package.json
├── LICENSE
├── tailwind.config.js
└── README.md
/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ARYPROGRAMMER/Video-Generator-AI/HEAD/app/favicon.ico
--------------------------------------------------------------------------------
/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ARYPROGRAMMER/Video-Generator-AI/HEAD/public/logo.png
--------------------------------------------------------------------------------
/public/soon.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ARYPROGRAMMER/Video-Generator-AI/HEAD/public/soon.gif
--------------------------------------------------------------------------------
/public/star.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ARYPROGRAMMER/Video-Generator-AI/HEAD/public/star.png
--------------------------------------------------------------------------------
/public/comic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ARYPROGRAMMER/Video-Generator-AI/HEAD/public/comic.png
--------------------------------------------------------------------------------
/public/login.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ARYPROGRAMMER/Video-Generator-AI/HEAD/public/login.png
--------------------------------------------------------------------------------
/public/cartoon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ARYPROGRAMMER/Video-Generator-AI/HEAD/public/cartoon.png
--------------------------------------------------------------------------------
/public/cyberpunk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ARYPROGRAMMER/Video-Generator-AI/HEAD/public/cyberpunk.png
--------------------------------------------------------------------------------
/public/realistic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ARYPROGRAMMER/Video-Generator-AI/HEAD/public/realistic.png
--------------------------------------------------------------------------------
/app/fonts/GeistVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ARYPROGRAMMER/Video-Generator-AI/HEAD/app/fonts/GeistVF.woff
--------------------------------------------------------------------------------
/public/watercolor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ARYPROGRAMMER/Video-Generator-AI/HEAD/public/watercolor.png
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@/*": ["./*"]
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/screenshots/image (1).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ARYPROGRAMMER/Video-Generator-AI/HEAD/screenshots/image (1).png
--------------------------------------------------------------------------------
/screenshots/image (2).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ARYPROGRAMMER/Video-Generator-AI/HEAD/screenshots/image (2).png
--------------------------------------------------------------------------------
/screenshots/image (3).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ARYPROGRAMMER/Video-Generator-AI/HEAD/screenshots/image (3).png
--------------------------------------------------------------------------------
/screenshots/image (4).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ARYPROGRAMMER/Video-Generator-AI/HEAD/screenshots/image (4).png
--------------------------------------------------------------------------------
/screenshots/image (5).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ARYPROGRAMMER/Video-Generator-AI/HEAD/screenshots/image (5).png
--------------------------------------------------------------------------------
/screenshots/image (6).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ARYPROGRAMMER/Video-Generator-AI/HEAD/screenshots/image (6).png
--------------------------------------------------------------------------------
/screenshots/image (7).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ARYPROGRAMMER/Video-Generator-AI/HEAD/screenshots/image (7).png
--------------------------------------------------------------------------------
/screenshots/image (8).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ARYPROGRAMMER/Video-Generator-AI/HEAD/screenshots/image (8).png
--------------------------------------------------------------------------------
/screenshots/image (9).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ARYPROGRAMMER/Video-Generator-AI/HEAD/screenshots/image (9).png
--------------------------------------------------------------------------------
/app/fonts/GeistMonoVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ARYPROGRAMMER/Video-Generator-AI/HEAD/app/fonts/GeistMonoVF.woff
--------------------------------------------------------------------------------
/screenshots/image (10).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ARYPROGRAMMER/Video-Generator-AI/HEAD/screenshots/image (10).png
--------------------------------------------------------------------------------
/screenshots/image (11).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ARYPROGRAMMER/Video-Generator-AI/HEAD/screenshots/image (11).png
--------------------------------------------------------------------------------
/screenshots/image (12).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ARYPROGRAMMER/Video-Generator-AI/HEAD/screenshots/image (12).png
--------------------------------------------------------------------------------
/app/_context/UserDetailContext.jsx:
--------------------------------------------------------------------------------
1 | import { createContext } from "react";
2 |
3 | export const UserDetailContext = createContext();
--------------------------------------------------------------------------------
/app/_context/VideoDataContext.jsx:
--------------------------------------------------------------------------------
1 | import { createContext } from "react";
2 |
3 | export const VideoDataContext = createContext();
--------------------------------------------------------------------------------
/remotion/index.jsx:
--------------------------------------------------------------------------------
1 | import { registerRoot } from "remotion";
2 | import { RemotionRoot } from "./Root";
3 |
4 | registerRoot(RemotionRoot);
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/utils.js:
--------------------------------------------------------------------------------
1 | import { clsx } from "clsx"
2 | import { twMerge } from "tailwind-merge"
3 |
4 | export function cn(...inputs) {
5 | return twMerge(clsx(inputs))
6 | }
7 |
--------------------------------------------------------------------------------
/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/configs/db.js:
--------------------------------------------------------------------------------
1 | import { neon } from '@neondatabase/serverless';
2 | import { drizzle } from 'drizzle-orm/neon-http';
3 | const sql = neon(process.env.NEXT_PUBLIC_DRIZZLE_DATABASE_URL);
4 | export const db = drizzle(sql);
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | images: {
4 | remotePatterns: [{hostname: 'images.unsplash.com'}, {hostname:'avatars.githubusercontent.com'},{hostname: 'vimeo.com'}], // Allow images from Unsplash
5 | }
6 | };
7 |
8 | export default nextConfig;
9 |
--------------------------------------------------------------------------------
/drizzle.config.js:
--------------------------------------------------------------------------------
1 | /** @type { import("drizzle-kit").Config } */
2 | export default {
3 | schema: "./configs/schema.js",
4 | dialect: "postgresql",
5 | dbCredentials: {
6 | url: "postgresql://video-app-db_owner:MI9jTZCsxae1@ep-delicate-night-a8ocfy5m.eastus2.azure.neon.tech/video-app-db?sslmode=require",
7 | }
8 | };
--------------------------------------------------------------------------------
/public/window.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/file.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/remotion/Root.jsx:
--------------------------------------------------------------------------------
1 | import RemotionVideo from '@/app/dashboard/_components/RemotionVideo'
2 | import React from 'react'
3 | import { Composition } from 'remotion'
4 |
5 | function RemotionRoot() {
6 | return (
7 |
8 |
16 |
17 | )
18 | }
19 |
20 | export default RemotionRoot
--------------------------------------------------------------------------------
/app/api/get-video-script/route.jsx:
--------------------------------------------------------------------------------
1 | import { generateCompletion } from "@/configs/aimodel";
2 | import { NextResponse } from "next/server";
3 |
4 |
5 | export async function POST(req){
6 |
7 | try {
8 | const { prompt } = await req.json();
9 | const completion = await generateCompletion(prompt);
10 | return new NextResponse(completion.choices[0].message.content, { status: 200 });
11 | } catch (e) {
12 |
13 | return new NextResponse({"Internal Server Error":e}, { status: 500 });
14 | }
15 | }
--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "new-york",
4 | "rsc": true,
5 | "tsx": false,
6 | "tailwind": {
7 | "config": "tailwind.config.js",
8 | "css": "app/globals.css",
9 | "baseColor": "neutral",
10 | "cssVariables": true,
11 | "prefix": ""
12 | },
13 | "aliases": {
14 | "components": "@/components",
15 | "utils": "@/lib/utils"
16 | },
17 | "registries": [
18 | {
19 | "name": "shadcn",
20 | "baseUrl": "https://ui.shadcn.com"
21 | }
22 | ]
23 | }
--------------------------------------------------------------------------------
/app/dashboard/_components/EmptyState.jsx:
--------------------------------------------------------------------------------
1 | import { Button } from '@/components/ui/button'
2 | import Link from 'next/link'
3 | import React from 'react'
4 |
5 | function EmptyState() {
6 | return (
7 |
8 |
9 | No Videos Created Yet
10 |
11 |
12 | Create New Video
13 |
14 |
15 | )
16 | }
17 |
18 | export default EmptyState
--------------------------------------------------------------------------------
/middleware.js:
--------------------------------------------------------------------------------
1 | import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";
2 |
3 | const isProtectedRoute = createRouteMatcher(['/dashboard(.*)'])
4 |
5 | export default clerkMiddleware(async (auth, req) => {
6 | if (isProtectedRoute(req)) await auth.protect()
7 | })
8 |
9 | export const config = {
10 | matcher: [
11 | // Skip Next.js internals and all static files, unless found in search params
12 | '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
13 | // Always run for API routes
14 | '/(api|trpc)(.*)',
15 | ],
16 | };
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.*
7 | .yarn/*
8 | !.yarn/patches
9 | !.yarn/plugins
10 | !.yarn/releases
11 | !.yarn/versions
12 |
13 | # testing
14 | /coverage
15 |
16 | # next.js
17 | /.next/
18 | /out/
19 |
20 | # production
21 | /build
22 |
23 | # misc
24 | .DS_Store
25 | *.pem
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 |
32 | # env files (can opt-in for commiting if needed)
33 | .env*
34 |
35 | # vercel
36 | .vercel
37 |
38 | # typescript
39 | *.tsbuildinfo
40 | next-env.d.ts
41 |
--------------------------------------------------------------------------------
/components/ui/textarea.jsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | import { cn } from "@/lib/utils"
4 |
5 | const Textarea = React.forwardRef(({ className, ...props }, ref) => {
6 | return (
7 | ()
14 | );
15 | })
16 | Textarea.displayName = "Textarea"
17 |
18 | export { Textarea }
19 |
--------------------------------------------------------------------------------
/app/api/generate-caption/route.jsx:
--------------------------------------------------------------------------------
1 | import { AssemblyAI } from "assemblyai";
2 | import { NextResponse } from "next/server";
3 |
4 | export async function POST(req){
5 |
6 | try{
7 | const {audioFileUrl}=await req.json()
8 |
9 | const client = new AssemblyAI({
10 | apiKey: process.env.NEXT_PUBLIC_CAPTION_API,
11 | });
12 |
13 | const FILE_URL = audioFileUrl;
14 | const data = {
15 | audio: FILE_URL
16 | }
17 |
18 | const transcript = await client.transcripts.transcribe(data);
19 |
20 | return NextResponse.json({'result':transcript.words})
21 | }
22 |
23 | catch(e){
24 | return NextResponse.json({'error':e})
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/configs/schema.js:
--------------------------------------------------------------------------------
1 | import { pgTable,serial,varchar,boolean ,json, integer} from "drizzle-orm/pg-core";
2 |
3 | export const Users=pgTable('users',{
4 | id: serial('id').primaryKey(),
5 | name: varchar('name').notNull(),
6 | email: varchar('email').notNull(),
7 | imageUrl: varchar('imageUrl'),
8 | subscription: boolean('subscription').default(false),
9 | credits: integer('credits').default(30),
10 | })
11 |
12 | export const VideoData=pgTable('videoData',
13 | {
14 | id: serial('id').primaryKey(),
15 | script: json('script').notNull(),
16 | audioFileUrl: varchar('audioFileUrl').notNull(),
17 | captions: json('captions').notNull(),
18 | imageList: varchar('imageList').array(),
19 | createdBy: varchar('createdBy').notNull(),
20 | }
21 | )
--------------------------------------------------------------------------------
/app/layout.js:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { Toaster } from "@/components/ui/sonner";
4 | import "./globals.css";
5 |
6 | import Provider from "./provider";
7 | import { ClerkProvider } from "@clerk/nextjs";
8 | import { Comfortaa } from "next/font/google";
9 |
10 | const metadata = {
11 | title: "Video Generator AI",
12 | description: "Generated by create next app",
13 | };
14 |
15 | const comfort = Comfortaa({ subsets: ["latin-ext"], style: "normal", weight: "700" });
16 |
17 | export default function RootLayout({ children }) {
18 | return (
19 |
20 |
21 |
22 |
23 | {children}
24 |
25 |
26 |
27 |
28 |
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/app/dashboard/create-new/_components/Loading.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {
3 | AlertDialog,
4 | AlertDialogContent,
5 | AlertDialogTitle,
6 | } from "@/components/ui/alert-dialog"
7 | import Image from 'next/image'
8 |
9 | function CustomLoading({loading}) {
10 | return (
11 |
12 |
13 |
14 |
15 |
16 |
17 | Generating your Video...Keep Calm
18 |
19 |
20 |
21 |
22 |
23 |
24 | )
25 | }
26 |
27 | export default CustomLoading
--------------------------------------------------------------------------------
/configs/FireBaseConfig.jsx:
--------------------------------------------------------------------------------
1 | // Import the functions you need from the SDKs you need
2 | import { initializeApp } from "firebase/app";
3 |
4 | import { getStorage } from "firebase/storage";
5 | // TODO: Add SDKs for Firebase products that you want to use
6 | // https://firebase.google.com/docs/web/setup#available-libraries
7 |
8 | // Your web app's Firebase configuration
9 | const firebaseConfig = {
10 | apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
11 | authDomain: "supercool-mental-health-app.firebaseapp.com",
12 | projectId: "supercool-mental-health-app",
13 | storageBucket: "supercool-mental-health-app.appspot.com",
14 | messagingSenderId: "26005611530",
15 | appId: "1:26005611530:web:3eeedbf3f32703373babf3"
16 | };
17 |
18 | // Initialize Firebase
19 | const app = initializeApp(firebaseConfig);
20 |
21 | export const storage=getStorage(app);
--------------------------------------------------------------------------------
/configs/aimodel.js:
--------------------------------------------------------------------------------
1 | import OpenAI from "openai";
2 |
3 |
4 | const openai = new OpenAI({
5 | apiKey: process.env.NEXT_PUBLIC_OPENAI_API_KEY,
6 | });
7 |
8 | export async function generateCompletion(prompt) {
9 | try {
10 | const completion = await openai.chat.completions.create({
11 | model: "gpt-4o",
12 | temperature: 0.5,
13 | top_p: 0.95,
14 | max_tokens: 8192,
15 | messages: [
16 | {
17 | role: "system",
18 | content: "You are a Video Script Writer and AI Image Prompt Engineer. You do all the tasks with sincerity.",
19 | },
20 | {
21 | role: "user",
22 | content: prompt,
23 | },
24 | ],
25 | });
26 |
27 | return completion;
28 | } catch (error) {
29 | console.error("Error in AI completion: ", error);
30 | throw error;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/provider.js:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { db } from '@/configs/db';
4 | import { useUser } from '@clerk/nextjs';
5 | import { Users } from '@/configs/schema';
6 | import { eq } from 'drizzle-orm';
7 | import React, { useEffect } from 'react';
8 |
9 | function Provider({children}) {
10 |
11 | const {user}=useUser();
12 |
13 | useEffect(()=>{
14 | user&&isNewUser();
15 | },[user]);
16 |
17 | const isNewUser= async ()=>{
18 | const result= await db.select().from(Users)
19 | .where(eq(Users.email,user?.primaryEmailAddress?.emailAddress));
20 |
21 | if (!result[0]){
22 | await db.insert(Users).values({
23 | name: user?.fullName,
24 | email: user?.primaryEmailAddress?.emailAddress,
25 | imageUrl: user?.imageUrl
26 | })
27 | }
28 |
29 |
30 | }
31 | return (
32 |
33 | {children}
34 |
35 | )
36 | }
37 |
38 | export default Provider
--------------------------------------------------------------------------------
/components/ui/sonner.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { useTheme } from "next-themes"
3 | import { Toaster as Sonner } from "sonner"
4 |
5 | const Toaster = ({
6 | ...props
7 | }) => {
8 | const { theme = "system" } = useTheme()
9 |
10 | return (
11 | ( )
26 | );
27 | }
28 |
29 | export { Toaster }
30 |
--------------------------------------------------------------------------------
/public/globe.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/dashboard/create-new/_components/SelectDuration.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {
3 | Select,
4 | SelectContent,
5 | SelectItem,
6 | SelectTrigger,
7 | SelectValue
8 | } from '@/components/ui/select'
9 |
10 | function SelectDuration({onUserSelect}) {
11 | return (
12 |
13 |
14 | Duration
15 |
16 |
17 | Select the Duration of the Video
18 |
19 |
20 |
21 | {
22 | value!='Custom Prompt' && onUserSelect('duration',value)
23 |
24 | }
25 | }>
26 |
27 |
28 |
29 |
30 |
31 | 15 Seconds
32 | 30 Seconds
33 | 60 Seconds
34 |
35 |
36 |
37 |
38 |
39 | )
40 | }
41 |
42 | export default SelectDuration
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/dashboard/_components/Header.jsx:
--------------------------------------------------------------------------------
1 | import { UserDetailContext } from '@/app/_context/UserDetailContext';
2 | import { Button } from '@/components/ui/button'
3 | import { UserButton } from '@clerk/nextjs'
4 | import Image from 'next/image'
5 | import { useRouter } from 'next/navigation';
6 | import React, { useContext } from 'react'
7 |
8 | function Header() {
9 | const router = useRouter();
10 | const {userDetail,setUserDetail}=useContext(UserDetailContext);
11 | return (
12 |
13 |
14 |
15 |
Video Generator AI
16 |
17 |
18 |
19 |
20 |
21 |
22 | {userDetail?.credits}
23 |
24 |
25 |
router.replace('/dashboard')}>
26 | DashBoard
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | )
35 | }
36 |
37 | export default Header
--------------------------------------------------------------------------------
/app/dashboard/layout.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import React, { useEffect, useState } from 'react'
4 | import Header from './_components/Header'
5 | import SideNav from './_components/SideNav'
6 | import { VideoDataContext } from '../_context/VideoDataContext'
7 | import { UserDetailContext } from '../_context/UserDetailContext'
8 | import { useUser } from '@clerk/nextjs'
9 | import { db } from '@/configs/db'
10 | import { eq } from 'drizzle-orm'
11 | import { Users } from '@/configs/schema'
12 |
13 |
14 | function DashboardLayout({children}) {
15 | const [videoData,setVideoData] = useState([]);
16 | const [userDetail,setUserDetail] = useState([]);
17 | const {user} = useUser();
18 |
19 | useEffect(()=>{
20 | user&&getUserDetail()
21 | },[user])
22 |
23 |
24 | const getUserDetail = async()=>{
25 | const result = await db.select().from(Users)
26 | .where(eq(Users.email,user?.primaryEmailAddress?.emailAddress));
27 | setUserDetail(result[0]);
28 |
29 | }
30 | return (
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | {children}
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | )
52 | }
53 |
54 | export default DashboardLayout
--------------------------------------------------------------------------------
/app/dashboard/_components/SideNav.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { CircleUser, FileVideo, PanelsTopLeft, ShieldPlus } from 'lucide-react'
4 | import Link from 'next/link'
5 | import { usePathname } from 'next/navigation'
6 | import React from 'react'
7 |
8 | function SideNav() {
9 |
10 | const MenuOption=[
11 | {
12 | id:1,
13 | name:'Dashboard',
14 | path:'/dashboard',
15 | icon:PanelsTopLeft
16 | } ,
17 | {
18 | id:2,
19 | name:'Create New',
20 | path:'/dashboard/create-new',
21 | icon:FileVideo
22 | } ,
23 | {
24 | id:3,
25 | name:'Upgrade',
26 | path:'/',
27 | icon:ShieldPlus
28 | } ,
29 | {
30 | id:4,
31 | name:'Account',
32 | path:'/',
33 | icon:CircleUser
34 | }
35 | ]
36 | const path=usePathname();
37 | return (
38 |
39 |
40 | {MenuOption.map((item,index)=>(
41 |
42 |
48 |
49 |
{item.name}
50 |
51 |
52 | ))}
53 |
54 |
55 |
56 |
57 | )
58 | }
59 |
60 | export default SideNav
--------------------------------------------------------------------------------
/app/dashboard/page.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import { Button } from '@/components/ui/button'
3 | import React,{useContext, useEffect, useState} from 'react'
4 | import { Open_Sans } from 'next/font/google'
5 | import EmptyState from './_components/EmptyState';
6 | import Link from 'next/link';
7 | import { db } from '@/configs/db';
8 | import { VideoData } from '@/configs/schema';
9 | import { eq } from 'drizzle-orm';
10 | import { useUser } from '@clerk/nextjs';
11 | import VideoList from './_components/VideoList';
12 |
13 |
14 | const open_sans = Open_Sans({subsets: ["latin-ext"],style:"normal",weight: "700"});
15 |
16 | function Dashboard() {
17 |
18 | const [videoList,setVideoList] = useState([]);
19 |
20 |
21 | const {user}=useUser();
22 |
23 |
24 | useEffect(()=>{
25 | user&&GetVideoList();
26 | },[user])
27 |
28 | const GetVideoList=async()=>{
29 | const result = await db.select().from(VideoData)
30 | .where(eq(VideoData?.createdBy,user?.primaryEmailAddress?.emailAddress))
31 |
32 | setVideoList(result);
33 | }
34 |
35 |
36 | return (
37 |
38 |
39 |
40 | Dashboard
41 |
42 |
43 | Create New +
44 |
45 |
46 |
47 | {/* Empty State */}
48 | {videoList?.length==0&&
49 |
50 |
}
51 |
52 | {/* List of Videos */}
53 |
54 |
55 | )
56 | }
57 |
58 | export default Dashboard
--------------------------------------------------------------------------------
/app/dashboard/_components/VideoList.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { Thumbnail } from "@remotion/player";
3 | import RemotionVideo from './RemotionVideo';
4 | import PlayerDialog from './PlayerDialog';
5 |
6 | function VideoList({videoList}) {
7 | const [openPlayDialog,setOpenPlayDialog]=useState(false);
8 | const [videoid,setVideoid]=useState();
9 |
10 | return (
11 |
12 | {videoList?.map((video,index)=>(
13 |
{setOpenPlayDialog(Date.now());setVideoid(video?.id)}}>
15 | console.log(v)
33 | }}
34 | />
35 |
36 |
37 |
38 | ))}
39 |
40 | )
41 | }
42 |
43 | export default VideoList
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "video-generation",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint",
10 | "db:push": "drizzle-kit push",
11 | "db:studio": "drizzle-kit studio"
12 | },
13 | "dependencies": {
14 | "@clerk/nextjs": "^6.0.2",
15 | "@google-cloud/text-to-speech": "^5.5.0",
16 | "@google/generative-ai": "^0.21.0",
17 | "@neondatabase/serverless": "^0.10.1",
18 | "@radix-ui/react-alert-dialog": "^1.1.2",
19 | "@radix-ui/react-dialog": "^1.1.2",
20 | "@radix-ui/react-icons": "^1.3.0",
21 | "@radix-ui/react-select": "^2.1.2",
22 | "@radix-ui/react-slot": "^1.1.0",
23 | "@remotion/cli": "4.0.223",
24 | "@splinetool/react-spline": "^4.0.0",
25 | "@splinetool/runtime": "^1.9.35",
26 | "add": "^2.0.6",
27 | "assemblyai": "^4.7.1",
28 | "axios": "^1.7.7",
29 | "class-variance-authority": "^0.7.0",
30 | "clsx": "^2.1.1",
31 | "dialog": "^0.3.1",
32 | "drizzle-orm": "^0.35.3",
33 | "elevenlabs": "^0.17.1",
34 | "firebase": "^11.0.1",
35 | "framer-motion": "^11.11.10",
36 | "lucide-react": "^0.454.0",
37 | "next": "15.0.1",
38 | "next-themes": "^0.3.0",
39 | "openai": "^4.68.4",
40 | "react": "19.0.0-rc-69d4b800-20241021",
41 | "react-dom": "19.0.0-rc-69d4b800-20241021",
42 | "remotion": "4.0.223",
43 | "shadcn-cli": "^0.0.6",
44 | "simple-icons": "^13.15.0",
45 | "sonner": "^1.5.0",
46 | "tailwind-merge": "^2.5.4",
47 | "tailwindcss-animate": "^1.0.7",
48 | "uuid": "^11.0.2"
49 | },
50 | "devDependencies": {
51 | "drizzle-kit": "^0.26.2",
52 | "postcss": "^8",
53 | "tailwindcss": "^3.4.1"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/components/ui/button.jsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { Slot } from "@radix-ui/react-slot"
3 | import { cva } from "class-variance-authority";
4 |
5 | import { cn } from "@/lib/utils"
6 |
7 | const buttonVariants = cva(
8 | "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
9 | {
10 | variants: {
11 | variant: {
12 | default:
13 | "bg-primary text-primary-foreground shadow hover:bg-primary/90",
14 | destructive:
15 | "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
16 | outline:
17 | "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
18 | secondary:
19 | "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
20 | ghost: "hover:bg-accent hover:text-accent-foreground",
21 | link: "text-primary underline-offset-4 hover:underline",
22 | },
23 | size: {
24 | default: "h-9 px-4 py-2",
25 | sm: "h-8 rounded-md px-3 text-xs",
26 | lg: "h-10 rounded-md px-8",
27 | icon: "h-9 w-9",
28 | },
29 | },
30 | defaultVariants: {
31 | variant: "default",
32 | size: "default",
33 | },
34 | }
35 | )
36 |
37 | const Button = React.forwardRef(({ className, variant, size, asChild = false, ...props }, ref) => {
38 | const Comp = asChild ? Slot : "button"
39 | return (
40 | ( )
44 | );
45 | })
46 | Button.displayName = "Button"
47 |
48 | export { Button, buttonVariants }
49 |
--------------------------------------------------------------------------------
/app/dashboard/create-new/_components/SelectTopic.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import React,{useState} from 'react'
3 | import {
4 | Select,
5 | SelectContent,
6 | SelectItem,
7 | SelectTrigger,
8 | SelectValue
9 | } from '@/components/ui/select'
10 | import { Textarea } from '@/components/ui/textarea'
11 |
12 | function SelectTopic({onUserSelect}) {
13 | const options=[
14 | 'Custom Prompt',
15 | 'Random AI Story',
16 | 'Scary Story',
17 | 'Historical Facts',
18 | 'Bed Time Story',
19 | 'Motivational',
20 | 'Fun Facts'
21 | ]
22 |
23 | const [selectedOption, setSelectedOption] = useState()
24 |
25 | return (
26 |
27 |
28 | Content
29 |
30 |
31 | Select the topic of your Content
32 |
33 |
34 |
35 | {
36 | setSelectedOption(value)
37 | value!='Custom Prompt' && onUserSelect('topic',value)
38 |
39 | }
40 | }>
41 |
42 |
43 |
44 |
45 | {options.map((item,index)=>(
46 | {item}
47 | ))}
48 |
49 |
50 |
51 |
52 | {
53 |
54 | selectedOption=='Custom Prompt' &&
55 |
61 | )
62 | }
63 |
64 | export default SelectTopic
--------------------------------------------------------------------------------
/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | :root {
7 | --background: 0 0% 100%;
8 | --foreground: 0 0% 3.9%;
9 | --card: 0 0% 100%;
10 | --card-foreground: 0 0% 3.9%;
11 | --popover: 0 0% 100%;
12 | --popover-foreground: 0 0% 3.9%;
13 | --primary: 0 0% 9%;
14 | --primary-foreground: 0 0% 98%;
15 | --secondary: 0 0% 96.1%;
16 | --secondary-foreground: 0 0% 9%;
17 | --muted: 0 0% 96.1%;
18 | --muted-foreground: 0 0% 45.1%;
19 | --accent: 0 0% 96.1%;
20 | --accent-foreground: 0 0% 9%;
21 | --destructive: 0 84.2% 60.2%;
22 | --destructive-foreground: 0 0% 98%;
23 | --border: 0 0% 89.8%;
24 | --input: 0 0% 89.8%;
25 | --ring: 0 0% 3.9%;
26 | --radius: 0.5rem;
27 | --chart-1: 12 76% 61%;
28 | --chart-2: 173 58% 39%;
29 | --chart-3: 197 37% 24%;
30 | --chart-4: 43 74% 66%;
31 | --chart-5: 27 87% 67%;
32 | }
33 |
34 | .dark {
35 | --background: 0 0% 3.9%;
36 | --foreground: 0 0% 98%;
37 | --card: 0 0% 3.9%;
38 | --card-foreground: 0 0% 98%;
39 | --popover: 0 0% 3.9%;
40 | --popover-foreground: 0 0% 98%;
41 | --primary: 0 0% 98%;
42 | --primary-foreground: 0 0% 9%;
43 | --secondary: 0 0% 14.9%;
44 | --secondary-foreground: 0 0% 98%;
45 | --muted: 0 0% 14.9%;
46 | --muted-foreground: 0 0% 63.9%;
47 | --accent: 0 0% 14.9%;
48 | --accent-foreground: 0 0% 98%;
49 | --destructive: 0 62.8% 30.6%;
50 | --destructive-foreground: 0 0% 98%;
51 | --border: 0 0% 14.9%;
52 | --input: 0 0% 14.9%;
53 | --ring: 0 0% 83.1%;
54 | --chart-1: 220 70% 50%;
55 | --chart-2: 160 60% 45%;
56 | --chart-3: 30 80% 55%;
57 | --chart-4: 280 65% 60%;
58 | --chart-5: 340 75% 55%;
59 | }
60 | }
61 |
62 | @layer base {
63 | * {
64 | @apply border-border;
65 | }
66 | body {
67 | background-color: #e7f4fd;
68 | /* @apply bg-background text-foreground; */
69 | }
70 | }
--------------------------------------------------------------------------------
/app/api/generate-image/route.jsx:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { NextResponse } from 'next/server';
3 | import { getDownloadURL, ref, uploadBytes } from "firebase/storage";
4 | import { storage } from "@/configs/FireBaseConfig";
5 |
6 | export async function POST(req) {
7 | try {
8 |
9 | const { prompt } = await req.json();
10 |
11 | if (!prompt) {
12 | return NextResponse.json(
13 | { error: 'Prompt is required' },
14 | { status: 400 }
15 | );
16 | }
17 |
18 | const response = await axios.post(
19 | 'https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-3.5-large',
20 | {
21 | inputs: prompt,
22 | parameters: {
23 | height: 1024,
24 | width: 1024
25 | }
26 | },
27 | {
28 | headers: {
29 | Authorization: `Bearer ${process.env.HUGGINGFACE_API_KEY}`,
30 | 'Content-Type': 'application/json',
31 | },
32 | responseType: 'arraybuffer'
33 | }
34 | );
35 |
36 | const timestamp = Date.now();
37 | const filename = `generated-images/${timestamp}-${prompt.slice(0, 30)}.png`;
38 |
39 | const storageRef = ref(storage, filename);
40 |
41 | const blob = new Blob([response.data], { type: 'image/png' });
42 |
43 | await uploadBytes(storageRef, blob);
44 |
45 | const downloadURL = await getDownloadURL(storageRef);
46 |
47 | return NextResponse.json(
48 | {
49 | result: downloadURL
50 | },
51 | {
52 | status: 200,
53 | headers: {
54 | 'Cache-Control': 'public, max-age=31536000',
55 | }
56 | }
57 | );
58 |
59 | } catch (error) {
60 | return NextResponse.json(
61 | { error: 'Error generating image' },
62 | { status: 500 }
63 | );
64 | }
65 | }
--------------------------------------------------------------------------------
/app/dashboard/_components/PlayerDialog.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import {Player} from '@remotion/player';
3 |
4 | import {
5 | Dialog,
6 | DialogContent,
7 | DialogTitle,
8 |
9 | } from "@/components/ui/dialog"
10 |
11 | import RemotionVideo from './RemotionVideo';
12 | import { Button } from '@/components/ui/button';
13 | import { db } from '@/configs/db';
14 | import { VideoData } from '@/configs/schema';
15 | import { eq } from 'drizzle-orm';
16 | import { useRouter } from 'next/navigation';
17 |
18 |
19 | function PlayerDialog({playVideo,videoid}) {
20 | const [openDialog,setOpenDialog]=useState(true);
21 | const [videoData,setVideoData]=useState();
22 | const [durationinFrames,setDurationinFrames]=useState(100);
23 | const router = useRouter();
24 |
25 | useEffect(() => {
26 | setOpenDialog(!openDialog)
27 | videoid&&GetVideoData();
28 |
29 | },[playVideo])
30 |
31 | const GetVideoData = async ()=>{
32 | const result = await db.select().from(VideoData)
33 | .where(eq(VideoData.id,videoid));
34 | setVideoData(result[0]);
35 |
36 | }
37 | return (
38 |
39 |
40 |
41 |
42 | Your Video is Generated
43 | setDurationinFrames(frameValue)
53 | }}
54 | />
55 |
56 | {router.replace('/dashboard');setOpenDialog(false)}}>
57 | Cancel
58 |
59 |
60 | Export
61 |
62 |
63 |
64 |
65 |
66 | )
67 | }
68 |
69 | export default PlayerDialog
--------------------------------------------------------------------------------
/public/devpost.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
8 |
9 |
10 |
12 |
13 |
15 |
17 |
20 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2024 Arya Pratap Singh
5 |
6 | Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for software and other kinds of works.
11 |
12 | The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too.
13 |
14 | When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.
15 |
16 | To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.
17 |
18 | For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
19 |
20 | Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute, and/or modify it.
21 |
22 | For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.
23 |
24 | ...
25 |
26 | END OF TERMS AND CONDITIONS
27 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | darkMode: ["class"],
4 | content: [
5 | './pages/**/*.{js,jsx}',
6 | './components/**/*.{js,jsx}',
7 | './app/**/*.{js,jsx}',
8 | './src/**/*.{js,jsx}',
9 | ],
10 | prefix: "",
11 | theme: {
12 | container: {
13 | center: true,
14 | padding: "2rem",
15 | screens: {
16 | "2xl": "1400px",
17 | },
18 | },
19 | extend: {
20 | colors: {
21 | border: "hsl(var(--border))",
22 | input: "hsl(var(--input))",
23 | ring: "hsl(var(--ring))",
24 | background: "hsl(var(--background))",
25 | foreground: "hsl(var(--foreground))",
26 | primary: {
27 | DEFAULT: "#8681f4",
28 | foreground: "hsl(var(--primary-foreground))",
29 | },
30 | secondary: {
31 | DEFAULT: "hsl(var(--secondary))",
32 | foreground: "hsl(var(--secondary-foreground))",
33 | },
34 | destructive: {
35 | DEFAULT: "hsl(var(--destructive))",
36 | foreground: "hsl(var(--destructive-foreground))",
37 | },
38 | muted: {
39 | DEFAULT: "hsl(var(--muted))",
40 | foreground: "hsl(var(--muted-foreground))",
41 | },
42 | accent: {
43 | DEFAULT: "hsl(var(--accent))",
44 | foreground: "hsl(var(--accent-foreground))",
45 | },
46 | popover: {
47 | DEFAULT: "hsl(var(--popover))",
48 | foreground: "hsl(var(--popover-foreground))",
49 | },
50 | card: {
51 | DEFAULT: "hsl(var(--card))",
52 | foreground: "hsl(var(--card-foreground))",
53 | },
54 | },
55 | borderRadius: {
56 | lg: "var(--radius)",
57 | md: "calc(var(--radius) - 2px)",
58 | sm: "calc(var(--radius) - 4px)",
59 | },
60 | keyframes: {
61 | "accordion-down": {
62 | from: { height: "0" },
63 | to: { height: "var(--radix-accordion-content-height)" },
64 | },
65 | "accordion-up": {
66 | from: { height: "var(--radix-accordion-content-height)" },
67 | to: { height: "0" },
68 | },
69 | },
70 | animation: {
71 | "accordion-down": "accordion-down 0.2s ease-out",
72 | "accordion-up": "accordion-up 0.2s ease-out",
73 | },
74 | },
75 | },
76 | plugins: [require("tailwindcss-animate")],
77 | }
--------------------------------------------------------------------------------
/app/api/generate-audio-file/route.jsx:
--------------------------------------------------------------------------------
1 | import { ElevenLabsClient } from "elevenlabs";
2 | import { NextResponse } from "next/server";
3 | import { getDownloadURL, ref, uploadBytes } from "firebase/storage";
4 | import { storage } from "@/configs/FireBaseConfig";
5 |
6 | export async function POST(req) {
7 | try {
8 |
9 | const { text, id } = await req.json();
10 |
11 | if (!text || !id) {
12 | return NextResponse.json(
13 | { error: "Missing required fields: text and id" },
14 | { status: 400 }
15 | );
16 | }
17 | const apiKey = process.env.NEXT_PUBLIC_ELEVEN_LABS_API_KEY;
18 | if (!apiKey) {
19 | throw new Error("ElevenLabs API key is not configured");
20 | }
21 | const elevenlabs = new ElevenLabsClient({
22 | apiKey: apiKey,
23 | });
24 |
25 | const audioStream = await elevenlabs.generate({
26 |
27 | voice: "Jessica" ,
28 | text: text,
29 | modelId: "eleven_multilingual_v2",
30 | outputFormat: "mp3",
31 | });
32 | const chunks = [];
33 | for await (const chunk of audioStream) {
34 | chunks.push(chunk);
35 | }
36 | const audioBuffer = Buffer.concat(chunks);
37 |
38 | const storageRef = ref(storage,'video-generator-ai-files/'+id+'.mp3');
39 |
40 | await uploadBytes(storageRef, audioBuffer, { contentType: 'audio/mp3' });
41 |
42 | const downloadUrl = await getDownloadURL(storageRef);
43 |
44 | return NextResponse.json(
45 | {
46 | Result: downloadUrl
47 | },
48 | { status: 200 }
49 | );
50 |
51 | } catch (error) {
52 | console.error('Error details:', {
53 | message: error.message,
54 | statusCode: error.statusCode,
55 | body: error.body
56 | });
57 |
58 | let errorMessage = "Failed to generate audio";
59 | if (error.statusCode === 403) {
60 | errorMessage = "Authentication failed. Please check your ElevenLabs API key.";
61 | } else if (error.message.includes("API key")) {
62 | errorMessage = "ElevenLabs API key is missing or invalid";
63 | }
64 |
65 | return NextResponse.json(
66 | { error: errorMessage },
67 | { status: error.statusCode || 500 }
68 | );
69 | }
70 | }
--------------------------------------------------------------------------------
/app/dashboard/create-new/_components/SelectStyle.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import Image from 'next/image'
3 | import React, { useState } from 'react'
4 |
5 | function SelectStyle({onUserSelect}) {
6 |
7 | const styleOptions = [
8 | {
9 | name:"Realistic",
10 | image:'/realistic.png'
11 | },
12 | {
13 | name:"Cartoon",
14 | image:'/cartoon.png'
15 | },
16 | {
17 | name:"Comic",
18 | image:'/comic.png'
19 | },
20 | {
21 | name:"Watercolor",
22 | image:'/watercolor.png'
23 | },
24 | {
25 | name:"CyberPunk",
26 | image:'/cyberpunk.png'
27 | },
28 | ]
29 |
30 | const [selectedStyle, setSelectedStyle] = useState()
31 | return (
32 |
33 |
34 | Styles
35 |
36 |
37 | Select the Style for your Video
38 |
39 |
40 |
43 | {styleOptions.map((item,index)=>(
44 |
50 |
56 |
57 | {
58 | setSelectedStyle(item.name)
59 | onUserSelect('imageStyle',item.name)
60 | }
61 |
62 | }
63 |
64 | />
65 |
66 |
71 | {item.name}
72 |
73 |
74 | ))}
75 |
76 |
77 | )
78 | }
79 |
80 | export default SelectStyle
--------------------------------------------------------------------------------
/app/dashboard/_components/RemotionVideo.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import { AbsoluteFill, Img, Sequence, useVideoConfig, Audio, useCurrentFrame, interpolate } from 'remotion';
3 |
4 | function RemotionVideo({ script, audioFileUrl, captions, imageList, setDurationinFrames }) {
5 | const { fps } = useVideoConfig();
6 | const frame = useCurrentFrame();
7 |
8 | // Set duration in frames using useEffect to avoid state update during render
9 | useEffect(() => {
10 | if (captions?.length > 0) {
11 | const durationInFrames = captions[captions.length - 1].end / 1000 * fps;
12 | setDurationinFrames(durationInFrames);
13 | }
14 | }, [captions, fps, setDurationinFrames]);
15 |
16 | const getDurationFrames = () => {
17 | return captions[captions?.length - 1]?.end / 1000 * fps;
18 | };
19 |
20 | const getCurrentCaptions = () => {
21 | const currentTime = (frame / fps) * 1000;
22 | const currentCaption = captions.find((word) => currentTime >= word.start && currentTime <= word.end);
23 | return currentCaption ? currentCaption.text : '';
24 | };
25 |
26 | return script && (
27 |
28 | {imageList?.map((item, index) => {
29 | const startTime = (index * getDurationFrames()) / imageList?.length;
30 | const duration = getDurationFrames();
31 | const scale = (index) => interpolate(
32 | frame,
33 | [startTime, startTime + duration / 2, startTime + duration],
34 | index % 2 === 0 ? [1, 1.8, 1] : [1.8, 1, 1.8],
35 | {
36 | extrapolateLeft: 'clamp',
37 | extrapolateRight: 'clamp',
38 | }
39 | );
40 | return (
41 |
42 |
43 |
52 | {/* Explicit caption positioning at the bottom */}
53 |
65 |
66 | {getCurrentCaptions()}
67 |
68 |
69 |
70 |
71 | );
72 | })}
73 |
74 |
75 | );
76 | }
77 |
78 | export default RemotionVideo;
79 |
--------------------------------------------------------------------------------
/components/ui/dialog.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as DialogPrimitive from "@radix-ui/react-dialog"
5 | import { Cross2Icon } from "@radix-ui/react-icons"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const Dialog = DialogPrimitive.Root
10 |
11 | const DialogTrigger = DialogPrimitive.Trigger
12 |
13 | const DialogPortal = DialogPrimitive.Portal
14 |
15 | const DialogClose = DialogPrimitive.Close
16 |
17 | const DialogOverlay = React.forwardRef(({ className, ...props }, ref) => (
18 |
25 | ))
26 | DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
27 |
28 | const DialogContent = React.forwardRef(({ className, children, ...props }, ref) => (
29 |
30 |
31 |
38 | {children}
39 |
41 |
42 | Close
43 |
44 |
45 |
46 | ))
47 | DialogContent.displayName = DialogPrimitive.Content.displayName
48 |
49 | const DialogHeader = ({
50 | className,
51 | ...props
52 | }) => (
53 |
56 | )
57 | DialogHeader.displayName = "DialogHeader"
58 |
59 | const DialogFooter = ({
60 | className,
61 | ...props
62 | }) => (
63 |
66 | )
67 | DialogFooter.displayName = "DialogFooter"
68 |
69 | const DialogTitle = React.forwardRef(({ className, ...props }, ref) => (
70 |
74 | ))
75 | DialogTitle.displayName = DialogPrimitive.Title.displayName
76 |
77 | const DialogDescription = React.forwardRef(({ className, ...props }, ref) => (
78 |
82 | ))
83 | DialogDescription.displayName = DialogPrimitive.Description.displayName
84 |
85 | export {
86 | Dialog,
87 | DialogPortal,
88 | DialogOverlay,
89 | DialogTrigger,
90 | DialogClose,
91 | DialogContent,
92 | DialogHeader,
93 | DialogFooter,
94 | DialogTitle,
95 | DialogDescription,
96 | }
97 |
--------------------------------------------------------------------------------
/components/ui/alert-dialog.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
5 |
6 | import { cn } from "@/lib/utils"
7 | import { buttonVariants } from "@/components/ui/button"
8 |
9 | const AlertDialog = AlertDialogPrimitive.Root
10 |
11 | const AlertDialogTrigger = AlertDialogPrimitive.Trigger
12 |
13 | const AlertDialogPortal = AlertDialogPrimitive.Portal
14 |
15 | const AlertDialogOverlay = React.forwardRef(({ className, ...props }, ref) => (
16 |
23 | ))
24 | AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
25 |
26 | const AlertDialogContent = React.forwardRef(({ className, ...props }, ref) => (
27 |
28 |
29 |
36 |
37 | ))
38 | AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
39 |
40 | const AlertDialogHeader = ({
41 | className,
42 | ...props
43 | }) => (
44 |
47 | )
48 | AlertDialogHeader.displayName = "AlertDialogHeader"
49 |
50 | const AlertDialogFooter = ({
51 | className,
52 | ...props
53 | }) => (
54 |
57 | )
58 | AlertDialogFooter.displayName = "AlertDialogFooter"
59 |
60 | const AlertDialogTitle = React.forwardRef(({ className, ...props }, ref) => (
61 |
62 | ))
63 | AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
64 |
65 | const AlertDialogDescription = React.forwardRef(({ className, ...props }, ref) => (
66 |
70 | ))
71 | AlertDialogDescription.displayName =
72 | AlertDialogPrimitive.Description.displayName
73 |
74 | const AlertDialogAction = React.forwardRef(({ className, ...props }, ref) => (
75 |
76 | ))
77 | AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
78 |
79 | const AlertDialogCancel = React.forwardRef(({ className, ...props }, ref) => (
80 |
84 | ))
85 | AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
86 |
87 | export {
88 | AlertDialog,
89 | AlertDialogPortal,
90 | AlertDialogOverlay,
91 | AlertDialogTrigger,
92 | AlertDialogContent,
93 | AlertDialogHeader,
94 | AlertDialogFooter,
95 | AlertDialogTitle,
96 | AlertDialogDescription,
97 | AlertDialogAction,
98 | AlertDialogCancel,
99 | }
100 |
--------------------------------------------------------------------------------
/components/ui/select.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import {
5 | CaretSortIcon,
6 | CheckIcon,
7 | ChevronDownIcon,
8 | ChevronUpIcon,
9 | } from "@radix-ui/react-icons"
10 | import * as SelectPrimitive from "@radix-ui/react-select"
11 |
12 | import { cn } from "@/lib/utils"
13 |
14 | const Select = SelectPrimitive.Root
15 |
16 | const SelectGroup = SelectPrimitive.Group
17 |
18 | const SelectValue = SelectPrimitive.Value
19 |
20 | const SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => (
21 | span]:line-clamp-1",
25 | className
26 | )}
27 | {...props}>
28 | {children}
29 |
30 |
31 |
32 |
33 | ))
34 | SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
35 |
36 | const SelectScrollUpButton = React.forwardRef(({ className, ...props }, ref) => (
37 |
41 |
42 |
43 | ))
44 | SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
45 |
46 | const SelectScrollDownButton = React.forwardRef(({ className, ...props }, ref) => (
47 |
51 |
52 |
53 | ))
54 | SelectScrollDownButton.displayName =
55 | SelectPrimitive.ScrollDownButton.displayName
56 |
57 | const SelectContent = React.forwardRef(({ className, children, position = "popper", ...props }, ref) => (
58 |
59 |
69 |
70 |
73 | {children}
74 |
75 |
76 |
77 |
78 | ))
79 | SelectContent.displayName = SelectPrimitive.Content.displayName
80 |
81 | const SelectLabel = React.forwardRef(({ className, ...props }, ref) => (
82 |
86 | ))
87 | SelectLabel.displayName = SelectPrimitive.Label.displayName
88 |
89 | const SelectItem = React.forwardRef(({ className, children, ...props }, ref) => (
90 |
97 |
98 |
99 |
100 |
101 |
102 | {children}
103 |
104 | ))
105 | SelectItem.displayName = SelectPrimitive.Item.displayName
106 |
107 | const SelectSeparator = React.forwardRef(({ className, ...props }, ref) => (
108 |
112 | ))
113 | SelectSeparator.displayName = SelectPrimitive.Separator.displayName
114 |
115 | export {
116 | Select,
117 | SelectGroup,
118 | SelectValue,
119 | SelectTrigger,
120 | SelectContent,
121 | SelectLabel,
122 | SelectItem,
123 | SelectSeparator,
124 | SelectScrollUpButton,
125 | SelectScrollDownButton,
126 | }
127 |
--------------------------------------------------------------------------------
/app/dashboard/create-new/page.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import React, { useContext, useEffect, useState } from 'react'
3 | import SelectTopic from './_components/SelectTopic'
4 | import SelectStyle from './_components/SelectStyle'
5 | import {Outfit} from 'next/font/google'
6 | import SelectDuration from './_components/SelectDuration'
7 | import { Button } from '@/components/ui/button'
8 | import axios from 'axios'
9 | import CustomLoading from './_components/Loading'
10 | import {v4 as uuidv4} from 'uuid'
11 | import { VideoDataContext } from '@/app/_context/VideoDataContext'
12 | import { Users, VideoData } from '@/configs/schema'
13 | import { useUser } from '@clerk/nextjs';
14 | import { db } from '@/configs/db'
15 | import PlayerDialog from '../_components/PlayerDialog'
16 | import { UserDetailContext } from '@/app/_context/UserDetailContext'
17 | import { toast } from 'sonner'
18 | import { eq } from 'drizzle-orm'
19 |
20 | const outfit = Outfit({subsets: ["latin-ext"],weight: "600"});
21 |
22 | function CreateNew() {
23 |
24 | const [formData,setFormData]= useState([]);
25 | const [loading,setLoading]=useState(false);
26 | const [videoScript,setVideoScript]=useState();
27 | const [audioFileUrl,setAudioFileUrl]=useState();
28 | const [caption,setCaption]=useState();
29 | const [imageList,setImageList]=useState();
30 | const {videoData,setVideoData} = useContext(VideoDataContext);
31 | const {user}=useUser();
32 | const [playVideo,setPlayVideo]=useState();
33 | const [videoid,setVideoid]=useState();
34 | const {userDetail,setUserDetail}=useContext(UserDetailContext);
35 |
36 |
37 |
38 | const onHandleChange=(fieldName,fieldValue)=>{
39 | setFormData(
40 | prev=>({
41 | ...prev,
42 | [fieldName]:fieldValue
43 | })
44 | )
45 | }
46 |
47 | const onCreateClickHandler=()=>{
48 | if (userDetail?.credits<=0)
49 | {
50 | console.log(userDetail?.credits);
51 | toast('You do not have enough credits to create a video. Please buy more credits to continue.');
52 | return;
53 | }
54 | GetVideoScript();
55 | }
56 |
57 | const GetVideoScript = async ()=>{
58 | setLoading(true)
59 | const fixedPrompt =
60 | "Write a Script that generates "+formData.duration+" video on the topic "+formData.topic+" along with AI Image prompt in "+formData.imageStyle+" Style for each scene and give me the result in JSON format with imagePrompt and contentText as field and without the word json or anything else, just the response. No Plain text";
61 | const result = await axios.post('/api/get-video-script',
62 | {prompt: fixedPrompt}
63 | );
64 |
65 | if (result.data){
66 | setVideoData(prev=>({
67 | ...prev,
68 | videoScript: result.data
69 | }))
70 |
71 | setVideoScript(result.data)
72 | await GetAudioFile(result.data);
73 | }
74 |
75 | }
76 |
77 | const GetAudioFile = async (videoScriptData)=>{
78 | let script = '';
79 | const id = uuidv4();
80 | videoScriptData.forEach(item=>{
81 | script=script+item.contentText+' ';
82 | })
83 | const responseaudio = await axios.post('/api/generate-audio-file',
84 | {
85 | text: script,
86 | id: id
87 | });
88 |
89 | setVideoData(prev=>({
90 | ...prev,
91 | 'audioFileUrl': responseaudio.data.Result
92 | }))
93 | setAudioFileUrl(responseaudio.data.Result);
94 | responseaudio.data.Result&& await GenerateAudioCaption(responseaudio.data.Result,videoScriptData);
95 | }
96 |
97 | const GenerateAudioCaption= async (fileUrl,videoScriptData)=>{
98 | const res = await axios.post('/api/generate-caption',
99 | {
100 | audioFileUrl: fileUrl
101 | }
102 | );
103 |
104 | setVideoData(prev=>({
105 | ...prev,
106 | 'captions': res.data.result
107 | }))
108 | setCaption(res?.data?.result);
109 | res.data.result&& await GenerateImage(videoScriptData);
110 |
111 | }
112 |
113 | const GenerateImage = async(videoScriptData)=>{
114 | let images=[];
115 | for (const element of videoScriptData){
116 | try{
117 | const res = await axios.post('/api/generate-image',
118 | {
119 | prompt: element.imagePrompt
120 | });
121 | images.push(res.data.result);
122 | }
123 | catch(e){
124 | console.log('ERROR'+e);
125 | }
126 | }
127 |
128 | setVideoData(prev=>({
129 | ...prev,
130 | 'imageList': images
131 | }))
132 | setImageList(images);
133 | setLoading(false);
134 | }
135 |
136 | useEffect(()=>{
137 | if (Object.keys(videoData).length==4){
138 | SaveVideoData(videoData);
139 | }
140 | },[videoData])
141 |
142 | const SaveVideoData = async (videoData)=>{
143 | setLoading(true);
144 | const result = await db.insert(VideoData).values({
145 | script: videoData?.videoScript,
146 | audioFileUrl: videoData?.audioFileUrl,
147 | captions: videoData?.captions,
148 | imageList: videoData?.imageList,
149 | createdBy: user?.primaryEmailAddress?.emailAddress
150 | }).returning({id:VideoData?.id})
151 |
152 | await UpdateUserCredits();
153 | setVideoid(result[0].id);
154 | setPlayVideo(true);
155 | setLoading(false);
156 | }
157 |
158 | const UpdateUserCredits=async()=>{
159 | const result =await db.update(Users).set({
160 | credits: userDetail?.credits-10
161 | }).where(eq(Users?.email,user?.primaryEmailAddress?.emailAddress))
162 |
163 | setUserDetail(prev=>({
164 | ...prev,
165 | "credits": userDetail?.credits-10
166 | }))
167 | }
168 |
169 | return (
170 |
171 |
Create A New Video
172 |
173 |
174 | {/* Select Topic */}
175 |
176 |
177 | {/* Select Style */}
178 |
179 |
180 | {/* Duration */}
181 |
182 |
183 | {/* Create Button */}
184 |
185 | Generate The Video
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 | )
194 | }
195 |
196 | export default CreateNew
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | # AI Video Generation SaaS Platform
5 | *Transform text into engaging videos with cutting-edge AI technology* ✨
6 |
7 | [](https://nextjs.org/)
8 | [](https://firebase.google.com/)
9 | [](https://openai.com/)
10 | [](https://clerk.dev/)
11 | [](https://www.typescriptlang.org/)
12 | [](https://www.postgresql.org/)
13 | [](https://orm.drizzle.team/)
14 | [](https://tailwindcss.com/)
15 | [](https://elevenlabs.io/)
16 | [](https://www.assemblyai.com/)
17 | [](https://gemini.com/)
18 | [](LICENSE)
19 | [](https://github.com/ARYPROGRAMMER/Video-Generator-AI/issues)
20 | [](https://github.com/ARYPROGRAMMER/Video-Generator-AI/pulls)
21 | [](https://github.com/ARYPROGRAMMER/Video-Generator-AI/graphs/contributors)
22 |
23 | [Demo Videos Generated](https://vimeo.com/1024767660) · [Demo Run](https://vimeo.com/1024864982?share=copy#t=0) · [Development Insights](https://vimeo.com/manage/videos/1024793348) · [Report Bug](https://github.com/ARYPROGRAMMER/Video-Generator-AI/issues) · [Request Feature](https://github.com/ARYPROGRAMMER/Video-Generator-AI/issues)
24 |
25 |
26 |
27 | ---
28 |
29 | ## Overview
30 |
31 | A modern **Next.js 15** application that transforms text queries into high-quality videos using cutting-edge AI technologies. Perfect for marketers, educators, and content creators looking to streamline their video content production.
32 |
33 |
34 | [](https://video-generator-h9fmg5fxf-aryprogrammers-projects.vercel.app/)
35 |
36 | [Common Exceptions while Running](https://vimeo.com/1025093493?share=copy)
37 |
38 | ## Screenshots
39 |
40 |
41 | View Screenshots
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | ### ✨ Key Features
62 |
63 | - **AI Video Generation** - Transform text to engaging videos
64 | - **Secure Authentication** - Powered by Clerk Auth
65 | - **Text-to-Speech** - High-quality voice synthesis with Eleven Labs
66 | - **Caption Generation** - Advanced audio processing with Assembly AI
67 | - **Modern UI** - Beautiful interface with shadcn/ui
68 | - **Database** - Robust data handling with Drizzle ORM
69 | - **AI Integration** - GPT-4 and Gemini 15 Flash
70 |
71 | [Link To DevPost Submission](https://devpost.com/software/video-generation-ai)
72 |
73 | ---
74 |
75 | ## Installation
76 |
77 | ### 1. Clone the Repository
78 |
79 | ```bash
80 | git clone https://github.com/ARYPROGRAMMER/Video-Generator-AI.git
81 | cd Video-Generator-AI
82 | ```
83 |
84 | ### 2. Install Dependencies
85 |
86 | ```bash
87 | npm install
88 | ```
89 |
90 | ### 3. Set up Environment Variables
91 |
92 | Create a `.env.local` file in the root directory and add the following variables. **Please note**: You must provide your own API keys as placeholder values have been removed for security.
93 |
94 | ```plaintext
95 | NEXT_PUBLIC_DRIZZLE_DATABASE_URL=postgresql://
96 | NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=
97 | CLERK_SECRET_KEY=
98 | NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
99 | NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
100 | NEXT_PUBLIC_ELEVEN_LABS_API_KEY=
101 | NEXT_PUBLIC_FIREBASE_API_KEY=
102 | NEXT_PUBLIC_CAPTION_API=
103 | HUGGINGFACE_API_KEY=
104 | NEXT_PUBLIC_OPENAI_API_KEY=
105 | ```
106 |
107 | > ⚠️ **Warning**: The app may not function correctly if the my API keys are expired or invalid. Please ensure your own valid API keys are added to avoid disruptions.
108 |
109 | ### 4. Run the App
110 |
111 | ```bash
112 | npm run dev
113 | ```
114 |
115 | The app will be running at [localhost:3000](http://localhost:3000)
116 |
117 | ---
118 |
119 | ## Contributing
120 |
121 | We welcome contributions! Please fork the repository, make your changes, and submit a pull request.
122 |
123 | ## License
124 |
125 | Distributed under the GNU GPL License. See [LICENSE](LICENSE) for more information.
126 |
127 |
--------------------------------------------------------------------------------
/app/(auth)/sign-in/[[...sign-in]]/page.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import React, { useState } from 'react';
3 | import { SignIn } from '@clerk/nextjs';
4 | import Image from 'next/image';
5 | import { Wand2, ArrowRight, Home, Play, ChevronRight, Sparkles, Video, Shield } from 'lucide-react';
6 | import { useRouter } from 'next/navigation';
7 | import { Button } from '@/components/ui/button';
8 | import { motion } from 'framer-motion';
9 |
10 | const SignInPage = () => {
11 | const router = useRouter();
12 | const [hoveredFeature, setHoveredFeature] = useState(null);
13 |
14 | const features = [
15 | { icon: Sparkles, text: "Instant Video Generation", delay: 0.1 },
16 | { icon: Video, text: "Custom AI Animations", delay: 0.2 },
17 | { icon: Shield, text: "Professional Templates", delay: 0.3 },
18 | { icon: Wand2, text: "High-Quality Exports", delay: 0.4 }
19 | ];
20 |
21 | const stats = [
22 | { value: "X K+", label: "Active Users", delay: 0.2 },
23 | { value: "Y K+", label: "Videos Created", delay: 0.3 },
24 | { value: "Z/5", label: "User Rating", delay: 0.4 }
25 | ];
26 |
27 | return (
28 |
29 | {/* Left Section */}
30 |
36 |
37 |
38 | {/* Top Brand Section */}
39 |
40 |
45 |
46 | Video Generator AI
47 |
48 |
52 | router.push('/')}
56 | >
57 |
58 | Home
59 |
60 |
61 |
62 |
63 | {/* Middle Content */}
64 |
65 |
71 |
75 |
80 |
81 |
82 | Welcome Back!
83 |
84 |
85 | Transform your ideas into captivating videos with the power of AI. Create professional content in minutes.
86 |
87 |
88 |
89 | {/* Features Grid */}
90 |
91 | {features.map((feature, index) => (
92 |
setHoveredFeature(index)}
99 | onMouseLeave={() => setHoveredFeature(null)}
100 | >
101 |
102 |
109 |
110 |
111 |
112 | {feature.text}
113 |
114 |
115 |
116 | ))}
117 |
118 |
119 |
120 | {/* Bottom Stats */}
121 |
122 | {stats.map((stat, index) => (
123 |
131 |
136 | {stat.value}
137 |
138 | {stat.label}
139 |
140 | ))}
141 |
142 |
143 |
144 |
145 |
146 | {/* Right Section */}
147 |
153 |
154 |
155 | {/* Mobile Header */}
156 |
161 |
162 |
163 | Video Generator AI
164 |
165 |
166 | router.push('/')}
170 | >
171 |
172 |
173 |
174 |
175 |
176 |
177 |
183 |
189 |
190 |
191 |
197 | Sign In to Create
198 |
199 |
205 | Your next amazing video awaits
206 |
207 |
208 |
209 |
215 |
216 |
217 |
218 |
224 |
225 |
226 |
227 | {/* Trust Badges */}
228 |
234 | Trusted by creators worldwide
235 |
236 | {[1, 2, 3].map((index) => (
237 |
245 | ))}
246 |
247 |
248 |
249 |
250 |
251 |
252 | );
253 | };
254 |
255 | export default SignInPage;
--------------------------------------------------------------------------------
/app/(auth)/sign-up/[[...sign-up]]/page.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import React, { useState } from 'react';
3 | import { SignUp } from '@clerk/nextjs';
4 | import { Wand2, Zap, Film, Share2, ArrowLeft, Sparkles } from 'lucide-react';
5 | import { motion } from 'framer-motion';
6 | import { useRouter } from 'next/navigation';
7 | import { Button } from '@/components/ui/button';
8 | import Image from 'next/image';
9 |
10 | const SignUpPage = () => {
11 | const router = useRouter();
12 | const [hoveredFeature, setHoveredFeature] = useState(null);
13 |
14 | const features = [
15 | {
16 | icon: Zap,
17 | title: "Lightning Fast Generation",
18 | description: "Create professional videos in under 5 minutes",
19 | delay: 0.2
20 | },
21 | {
22 | icon: Film,
23 | title: "4K Quality Output",
24 | description: "High-resolution videos ready for any platform",
25 |
26 | delay: 0.3
27 | },
28 | {
29 | icon: Share2,
30 | title: "Easy Sharing",
31 | description: "Direct export to social media platforms",
32 | delay: 0.4
33 | }
34 | ];
35 |
36 | const testimonials = [
37 | {
38 | image:"https://avatars.githubusercontent.com/u/75311742?v=4",
39 | name: "Arya Pratap Singh",
40 | role: "Software Engineer",
41 | quote: "Video Generator AI has taken a lot of Efforts out of my work. I can now focus on other important tasks while the AI does the video creation for me."
42 | },
43 | ];
44 |
45 |
46 | const floatingAnimation = {
47 | animate: {
48 | y: [0, -10, 0],
49 | transition: {
50 | duration: 5,
51 | repeat: Infinity,
52 | ease: "easeInOut"
53 | }
54 | }
55 | };
56 |
57 | return (
58 |
59 | {/* Left Section */}
60 |
66 |
74 |
75 |
81 |
82 | {/* Animated background elements */}
83 |
87 |
94 |
95 |
96 |
97 | {/* Brand Section */}
98 |
104 |
109 | router.push('/')}
113 | >
114 |
115 | Back to Home
116 |
117 |
118 |
119 |
124 |
125 | Create Stunning Videos with AI
126 |
127 |
128 | Transform your ideas into professional videos in minutes using the power of artificial intelligence
129 |
130 |
131 |
132 |
133 | {/* Features Grid */}
134 |
135 | {features.map((feature, index) => (
136 |
setHoveredFeature(index)}
144 | onMouseLeave={() => setHoveredFeature(null)}
145 | >
146 |
147 |
156 |
157 |
165 |
166 |
167 |
168 |
169 |
{feature.title}
170 |
{feature.description}
171 |
172 |
173 |
174 | ))}
175 |
176 |
177 | {/* Social Proof */}
178 |
184 |
185 |
186 | {[1, 2, 3,4,5,6].map((_, index) => (
187 |
194 |
197 |
198 |
199 |
200 | ))}
201 |
202 |
208 | Trusted by XY+ content creators
209 |
210 |
211 |
212 |
213 |
214 |
215 | {/* Right Section - Sign Up Form */}
216 |
222 |
223 |
224 | {/* Main Sign Up Card */}
225 |
231 |
237 |
238 |
244 |
245 | Start Creating Now
246 |
247 | Generate your first AI video in minutes
248 |
249 |
250 |
256 |
257 |
258 |
259 |
260 | {/* Testimonials Carousel */}
261 |
267 | {testimonials.map((testimonial, index) => (
268 |
276 |
277 |
278 |
285 |
286 |
287 |
{testimonial.name}
288 |
{testimonial.role}
289 |
290 |
291 | {testimonial.quote}
292 |
293 | ))}
294 |
295 |
296 | {/* Trust Indicators */}
297 |
303 |
309 | Powering next-generation video creation
310 |
311 |
317 | {[1, 2, 3].map((index) => (
318 |
324 | ))}
325 |
326 |
327 |
328 |
329 |
330 |
331 | );
332 | };
333 |
334 | export default SignUpPage;
--------------------------------------------------------------------------------
/app/page.js:
--------------------------------------------------------------------------------
1 | "use client"
2 | import React, { useEffect, useState } from 'react';
3 | import { motion } from "framer-motion";
4 | import { Button } from "@/components/ui/button";
5 | import { useAuth } from "@clerk/nextjs";
6 | import { useRouter } from "next/navigation";
7 | import { Wand2, Play, Sparkles, Zap, Share2, Layers, ArrowRight } from 'lucide-react';
8 | import { GitHubLogoIcon } from '@radix-ui/react-icons';
9 | import Image from 'next/image';
10 | import Spline from '@splinetool/react-spline';
11 |
12 | export default function Home() {
13 | const router = useRouter();
14 | const { isSignedIn, isLoaded } = useAuth();
15 | const [isPlaying, setIsPlaying] = useState(false);
16 | const videoUrl = "https://vimeo.com/1024767660";
17 |
18 | // Extract Vimeo video ID from the URL and generate embeddable URL
19 | const getVimeoEmbedUrl = (url) => {
20 | const videoId = url.split("vimeo.com/")[1];
21 | return `https://player.vimeo.com/video/${videoId}`;
22 | };
23 |
24 | const embedUrl = getVimeoEmbedUrl(videoUrl);
25 | const handlePlayClick = () => {
26 | setIsPlaying(true);
27 | };
28 |
29 | useEffect(() => {
30 | if (isLoaded && isSignedIn) {
31 | router.replace('/dashboard');
32 | }
33 | }, [isLoaded, isSignedIn, router]);
34 |
35 | const handleSignIn = () => {
36 | router.push('/dashboard');
37 | };
38 |
39 | if (!isLoaded) {
40 | return (
41 |
51 | );
52 | }
53 |
54 | return (
55 |
56 | {/* Spline background wrapper */}
57 |
58 |
59 |
60 |
61 | {/* Gradient overlay */}
62 |
63 |
64 |
65 | {/* Main content wrapper */}
66 |
67 |
73 |
74 |
75 |
76 |
77 | Video Generator AI
78 |
79 |
91 |
92 |
93 |
94 |
95 |
96 |
97 | {/* Hero Section */}
98 |
104 |
108 |
109 | AI-Powered Video Generation
110 |
111 |
112 | Create Stunning Videos with AI
113 |
114 |
115 | Transform your ideas into professional videos in minutes using cutting-edge AI technology.
116 | Perfect for marketing, education, and social media content.
117 |
118 |
119 |
123 | Start Creating
124 |
125 |
126 |
{
128 | window.open("https://vimeo.com/1024793348?share=copy", "_blank");
129 | }}
130 | variant="outline"
131 | className="border-purple-400 text-purple-400 hover:bg-purple-400/10 px-8 py-6 rounded-xl text-lg flex items-center space-x-2 w-full sm:w-auto transition-all transform hover:scale-105"
132 | >
133 |
134 | Watch Development Insights
135 |
136 |
137 |
138 |
139 | {/* Video Section */}
140 |
146 |
147 | {isPlaying ? (
148 |
156 | ) : (
157 | <>
158 |
159 |
160 |
161 |
162 |
168 |
169 |
See Example Videos Generated
170 |
Watch the videos generated during development
171 |
172 |
173 |
setIsPlaying(!isPlaying)}
177 | >
178 | Learn More
179 |
180 |
181 |
182 | >
183 | )}
184 |
185 |
186 |
187 | {/* Features Section */}
188 |
197 | {[
198 | {
199 | icon: ,
200 | title: "Lightning Fast",
201 | description: "Generate professional videos in under 5 minutes with our advanced AI technology"
202 | },
203 | {
204 | icon: ,
205 | title: "Multiple Styles",
206 | description: "Choose from various templates and styles to match your brand and message"
207 | },
208 | {
209 | icon: ,
210 | title: "Easy Sharing",
211 | description: "Export directly to social media platforms or download in multiple formats"
212 | }
213 | ].map((feature, index) => (
214 |
219 |
220 | {feature.icon}
221 |
222 | {feature.title}
223 | {feature.description}
224 |
225 | ))}
226 |
227 |
228 | {/* Stats Section */}
229 |
235 | {[
236 | { number: "UV K+", label: "Active Users" },
237 | { number: "WZ K+", label: "Videos Created" },
238 | { number: "MN/5", label: "User Rating" },
239 | { number: "24/7", label: "AI Support" }
240 | ].map((stat, index) => (
241 |
242 |
243 | {stat.number}
244 |
245 |
{stat.label}
246 |
247 | ))}
248 |
249 |
250 | {/* CTA Section */}
251 |
252 |
253 | Ready to Transform Your Content?
254 |
255 |
256 | Join thousands of creators who are already using Video Generator AI
257 | to create stunning content that engages their audience.
258 |
259 |
263 | Get Started for Free
264 |
265 |
266 |
267 |
268 |
269 | {/* Footer */}
270 |
271 |
272 |
273 |
274 |
275 | Video Generator AI
276 |
277 |
296 |
297 |
298 |
299 |
300 |
301 | );
302 | }
303 |
--------------------------------------------------------------------------------