From af9e3cc681df49e4eb01729990e2a8bce497a530 Mon Sep 17 00:00:00 2001 From: carlvellotti Date: Sat, 15 Feb 2025 12:14:32 -0800 Subject: [PATCH 001/121] Update page.tsx --- src/app/page.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index a41c2cd2..415d9fe5 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -9,9 +9,9 @@ export default function Home() {
-

Make anything you imagine 🪄

+

Meme Generator 🪄

- This whole page will be replaced when you run your template path. + Turn your ideas into viral memes with AI-powered suggestions

From fd680bc3789024f082f1d4f377b353cbd6ae62e0 Mon Sep 17 00:00:00 2001 From: carlvellotti Date: Sat, 15 Feb 2025 12:21:21 -0800 Subject: [PATCH 002/121] Update page.tsx --- src/app/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index 415d9fe5..99b8fcd6 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -9,7 +9,7 @@ export default function Home() {
-

Meme Generator 🪄

+

Meme Generator! 🪄

Turn your ideas into viral memes with AI-powered suggestions

From 9a43581500953ef46459407a27b11ef563200e06 Mon Sep 17 00:00:00 2001 From: carlvellotti Date: Sat, 15 Feb 2025 12:31:18 -0800 Subject: [PATCH 003/121] Delete route.ts Removed OpenAI dependencies to fix deployment --- src/app/api/openai/chat/route.ts | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 src/app/api/openai/chat/route.ts diff --git a/src/app/api/openai/chat/route.ts b/src/app/api/openai/chat/route.ts deleted file mode 100644 index 1790ba5b..00000000 --- a/src/app/api/openai/chat/route.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { openai } from "@ai-sdk/openai"; -import { convertToCoreMessages, streamText } from "ai"; - -export const runtime = "edge"; - -export async function POST(req: Request) { - const { messages } = await req.json(); - const result = await streamText({ - model: openai("gpt-4o"), - messages: convertToCoreMessages(messages), - system: "You are a helpful AI assistant", - }); - - return result.toDataStreamResponse(); -} From 5ec983e0a02b6182c6076a8c1b95a5e0ea7aaa7c Mon Sep 17 00:00:00 2001 From: carlvellotti Date: Sat, 15 Feb 2025 12:31:24 -0800 Subject: [PATCH 004/121] Delete route.ts Removed OpenAI dependencies to fix deployment --- src/app/api/openai/transcribe/route.ts | 38 -------------------------- 1 file changed, 38 deletions(-) delete mode 100644 src/app/api/openai/transcribe/route.ts diff --git a/src/app/api/openai/transcribe/route.ts b/src/app/api/openai/transcribe/route.ts deleted file mode 100644 index 2308ae09..00000000 --- a/src/app/api/openai/transcribe/route.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { NextResponse } from "next/server"; -import fs from "fs"; -import OpenAI from "openai"; - -const openai = new OpenAI(); - -export async function POST(req: Request) { - const body = await req.json(); - - const base64Audio = body.audio; - - // Convert the base64 audio data to a Buffer - const audio = Buffer.from(base64Audio, "base64"); - - // Define the file path for storing the temporary WAV file - const filePath = "tmp/input.wav"; - - try { - // Write the audio data to a temporary WAV file synchronously - fs.writeFileSync(filePath, audio); - - // Create a readable stream from the temporary WAV file - const readStream = fs.createReadStream(filePath); - - const data = await openai.audio.transcriptions.create({ - file: readStream, - model: "whisper-1", - }); - - // Remove the temporary file after successful processing - fs.unlinkSync(filePath); - - return NextResponse.json(data); - } catch (error) { - console.error("Error processing audio:", error); - return NextResponse.error(); - } -} From c57bd2f60ad8c4ce991b43ff69e57f59df303c74 Mon Sep 17 00:00:00 2001 From: carlvellotti Date: Sat, 15 Feb 2025 12:35:34 -0800 Subject: [PATCH 005/121] Update page.tsx --- src/app/page.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index 99b8fcd6..4c74d57a 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -9,9 +9,9 @@ export default function Home() {
-

Meme Generator! 🪄

+

Meme Generator 🪄

- Turn your ideas into viral memes with AI-powered suggestions + This whole page will be replaced when you run your template path.

From 0a0db9c75ea4199930032650b0ce39f54cff468d Mon Sep 17 00:00:00 2001 From: carlvellotti Date: Sat, 15 Feb 2025 12:39:52 -0800 Subject: [PATCH 006/121] Update page.tsx --- src/app/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index 4c74d57a..361e649c 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -11,7 +11,7 @@ export default function Home() {

Meme Generator 🪄

- This whole page will be replaced when you run your template path. + Create memes with AI!

From a8aad31837225e41775c51e85970eb8cee94d735 Mon Sep 17 00:00:00 2001 From: carlvellotti Date: Sat, 15 Feb 2025 13:52:03 -0800 Subject: [PATCH 007/121] Add initial meme generator components --- package-lock.json | 200 ++++++++++++++++++---- package.json | 3 + src/app/admin/upload/page.tsx | 119 +++++++++++++ src/app/api/generate-caption/route.ts | 19 ++ src/app/api/generate-suggestions/route.ts | 32 ++++ src/app/api/select-template/route.ts | 35 ++++ src/app/components/MemeDatabase.tsx | 42 +++++ src/app/components/MemeGenerator.tsx | 130 ++++++++++++++ src/app/components/Navigation.tsx | 25 +++ src/app/components/TemplateBrowser.tsx | 53 ++++++ src/app/globals.css | 23 +-- src/app/layout.tsx | 10 +- src/app/page.tsx | 54 ++---- src/lib/contexts/AuthContext.tsx | 70 ++++---- src/lib/supabase/admin.ts | 20 +++ src/lib/supabase/client.ts | 11 ++ src/lib/supabase/types.ts | 8 + src/lib/types/meme.ts | 19 ++ src/middleware.ts | 23 +++ 19 files changed, 768 insertions(+), 128 deletions(-) create mode 100644 src/app/admin/upload/page.tsx create mode 100644 src/app/api/generate-caption/route.ts create mode 100644 src/app/api/generate-suggestions/route.ts create mode 100644 src/app/api/select-template/route.ts create mode 100644 src/app/components/MemeDatabase.tsx create mode 100644 src/app/components/MemeGenerator.tsx create mode 100644 src/app/components/Navigation.tsx create mode 100644 src/app/components/TemplateBrowser.tsx create mode 100644 src/lib/supabase/admin.ts create mode 100644 src/lib/supabase/client.ts create mode 100644 src/lib/supabase/types.ts create mode 100644 src/lib/types/meme.ts create mode 100644 src/middleware.ts diff --git a/package-lock.json b/package-lock.json index 456fe0b4..f2bf701c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,10 @@ "dependencies": { "@ai-sdk/anthropic": "^0.0.48", "@ai-sdk/openai": "^0.0.54", + "@anthropic-ai/sdk": "^0.36.3", "@deepgram/sdk": "^3.6.0", + "@supabase/auth-helpers-nextjs": "^0.10.0", + "@supabase/supabase-js": "^2.48.1", "ai": "^3.3.20", "date-fns": "^3.6.0", "firebase": "^10.13.0", @@ -236,6 +239,36 @@ "node": ">=6.0.0" } }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.36.3", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.36.3.tgz", + "integrity": "sha512-+c0mMLxL/17yFZ4P5+U6bTWiCSFZUKJddrv01ud2aFBWnTPLdRncYV76D3q1tqfnL7aCnhRtykFnoCFzvr4U3Q==", + "license": "MIT", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + } + }, + "node_modules/@anthropic-ai/sdk/node_modules/@types/node": { + "version": "18.19.76", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.76.tgz", + "integrity": "sha512-yvR7Q9LdPz2vGpmpJX5LolrgRdWvB67MJKDPSgIIzpFbaf9a1j/f5DnLp5VDyHGMR0QZHlTr1afsD87QCXFHKw==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@anthropic-ai/sdk/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" + }, "node_modules/@babel/helper-string-parser": { "version": "7.24.8", "license": "MIT", @@ -1230,6 +1263,107 @@ "dev": true, "license": "MIT" }, + "node_modules/@supabase/auth-helpers-nextjs": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@supabase/auth-helpers-nextjs/-/auth-helpers-nextjs-0.10.0.tgz", + "integrity": "sha512-2dfOGsM4yZt0oS4TPiE7bD4vf7EVz7NRz/IJrV6vLg0GP7sMUx8wndv2euLGq4BjN9lUCpu6DG/uCC8j+ylwPg==", + "deprecated": "This package is now deprecated - please use the @supabase/ssr package instead.", + "license": "MIT", + "dependencies": { + "@supabase/auth-helpers-shared": "0.7.0", + "set-cookie-parser": "^2.6.0" + }, + "peerDependencies": { + "@supabase/supabase-js": "^2.39.8" + } + }, + "node_modules/@supabase/auth-helpers-shared": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@supabase/auth-helpers-shared/-/auth-helpers-shared-0.7.0.tgz", + "integrity": "sha512-FBFf2ei2R7QC+B/5wWkthMha8Ca2bWHAndN+syfuEUUfufv4mLcAgBCcgNg5nJR8L0gZfyuaxgubtOc9aW3Cpg==", + "deprecated": "This package is now deprecated - please use the @supabase/ssr package instead.", + "license": "MIT", + "dependencies": { + "jose": "^4.14.4" + }, + "peerDependencies": { + "@supabase/supabase-js": "^2.39.8" + } + }, + "node_modules/@supabase/auth-js": { + "version": "2.67.3", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.67.3.tgz", + "integrity": "sha512-NJDaW8yXs49xMvWVOkSIr8j46jf+tYHV0wHhrwOaLLMZSFO4g6kKAf+MfzQ2RaD06OCUkUHIzctLAxjTgEVpzw==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.4.tgz", + "integrity": "sha512-WL2p6r4AXNGwop7iwvul2BvOtuJ1YQy8EbOd0dhG1oN1q8el/BIRSFCFnWAMM/vJJlHWLi4ad22sKbKr9mvjoA==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/node-fetch": { + "version": "2.6.15", + "resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz", + "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/@supabase/postgrest-js": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.18.1.tgz", + "integrity": "sha512-dWDnoC0MoDHKhaEOrsEKTadWQcBNknZVQcSgNE/Q2wXh05mhCL1ut/jthRUrSbYcqIw/CEjhaeIPp7dLarT0bg==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.11.2.tgz", + "integrity": "sha512-u/XeuL2Y0QEhXSoIPZZwR6wMXgB+RQbJzG9VErA3VghVt7uRfSVsjeqd7m5GhX3JR6dM/WRmLbVR8URpDWG4+w==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14", + "@types/phoenix": "^1.5.4", + "@types/ws": "^8.5.10", + "ws": "^8.18.0" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.7.1.tgz", + "integrity": "sha512-asYHcyDR1fKqrMpytAS1zjyEfvxuOIp1CIXX7ji4lHHcJKqyk+sLl/Vxgm4sN6u8zvuUtae9e4kDxQP2qrwWBA==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.48.1", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.48.1.tgz", + "integrity": "sha512-VMD+CYk/KxfwGbI4fqwSUVA7CLr1izXpqfFerhnYPSi6LEKD8GoR4kuO5Cc8a+N43LnfSQwLJu4kVm2e4etEmA==", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.67.3", + "@supabase/functions-js": "2.4.4", + "@supabase/node-fetch": "2.6.15", + "@supabase/postgrest-js": "1.18.1", + "@supabase/realtime-js": "2.11.2", + "@supabase/storage-js": "2.7.1" + } + }, "node_modules/@swc/counter": { "version": "0.1.3", "license": "Apache-2.0" @@ -1297,13 +1431,17 @@ "node_modules/@types/node-fetch": { "version": "2.6.11", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { "@types/node": "*", "form-data": "^4.0.0" } }, + "node_modules/@types/phoenix": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.6.tgz", + "integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==", + "license": "MIT" + }, "node_modules/@types/prop-types": { "version": "15.7.12", "license": "MIT" @@ -1328,6 +1466,15 @@ "version": "3.0.3", "license": "MIT" }, + "node_modules/@types/ws": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz", + "integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/parser": { "version": "7.2.0", "dev": true, @@ -1567,7 +1714,6 @@ "node_modules/abort-controller": { "version": "3.0.0", "license": "MIT", - "optional": true, "dependencies": { "event-target-shim": "^5.0.0" }, @@ -1596,8 +1742,6 @@ "node_modules/agentkeepalive": { "version": "4.5.0", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { "humanize-ms": "^1.2.1" }, @@ -1895,9 +2039,7 @@ }, "node_modules/asynckit": { "version": "0.4.0", - "license": "MIT", - "optional": true, - "peer": true + "license": "MIT" }, "node_modules/available-typed-arrays": { "version": "1.0.7", @@ -2225,8 +2367,6 @@ "node_modules/combined-stream": { "version": "1.0.8", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -2479,8 +2619,6 @@ "node_modules/delayed-stream": { "version": "1.0.0", "license": "MIT", - "optional": true, - "peer": true, "engines": { "node": ">=0.4.0" } @@ -3175,7 +3313,6 @@ "node_modules/event-target-shim": { "version": "5.0.1", "license": "MIT", - "optional": true, "engines": { "node": ">=6" } @@ -3371,8 +3508,6 @@ "node_modules/form-data": { "version": "4.0.0", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -3384,15 +3519,11 @@ }, "node_modules/form-data-encoder": { "version": "1.7.2", - "license": "MIT", - "optional": true, - "peer": true + "license": "MIT" }, "node_modules/formdata-node": { "version": "4.4.1", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { "node-domexception": "1.0.0", "web-streams-polyfill": "4.0.0-beta.3" @@ -3774,17 +3905,13 @@ "node_modules/humanize-ms": { "version": "1.2.1", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { "ms": "^2.0.0" } }, "node_modules/humanize-ms/node_modules/ms": { "version": "2.1.3", - "license": "MIT", - "optional": true, - "peer": true + "license": "MIT" }, "node_modules/idb": { "version": "7.1.1", @@ -4347,6 +4474,15 @@ "jiti": "bin/jiti.js" } }, + "node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "license": "MIT" @@ -5115,8 +5251,6 @@ "node_modules/mime-db": { "version": "1.52.0", "license": "MIT", - "optional": true, - "peer": true, "engines": { "node": ">= 0.6" } @@ -5124,8 +5258,6 @@ "node_modules/mime-types": { "version": "2.1.35", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -5282,8 +5414,6 @@ } ], "license": "MIT", - "optional": true, - "peer": true, "engines": { "node": ">=10.5.0" } @@ -6232,6 +6362,12 @@ "semver": "bin/semver.js" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "dev": true, @@ -7088,8 +7224,6 @@ "node_modules/web-streams-polyfill": { "version": "4.0.0-beta.3", "license": "MIT", - "optional": true, - "peer": true, "engines": { "node": ">= 14" } diff --git a/package.json b/package.json index 1e6185cf..cbbf4bf8 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,10 @@ "dependencies": { "@ai-sdk/anthropic": "^0.0.48", "@ai-sdk/openai": "^0.0.54", + "@anthropic-ai/sdk": "^0.36.3", "@deepgram/sdk": "^3.6.0", + "@supabase/auth-helpers-nextjs": "^0.10.0", + "@supabase/supabase-js": "^2.48.1", "ai": "^3.3.20", "date-fns": "^3.6.0", "firebase": "^10.13.0", diff --git a/src/app/admin/upload/page.tsx b/src/app/admin/upload/page.tsx new file mode 100644 index 00000000..2dfbeb98 --- /dev/null +++ b/src/app/admin/upload/page.tsx @@ -0,0 +1,119 @@ +'use client'; + +import { useState } from 'react'; +import { supabase } from '@/lib/supabase/client'; + +export default function UploadPage() { + const [uploading, setUploading] = useState(false); + const [formData, setFormData] = useState({ + name: '', + instructions: '', + videoFile: null as File | null, + }); + + const handleUpload = async (e: React.FormEvent) => { + e.preventDefault(); + if (!formData.videoFile) return; + + try { + setUploading(true); + + // 1. Upload video to storage + const fileName = `${Date.now()}-${formData.videoFile.name}`; + const filePath = `templates/${fileName}`; + + const { error: uploadError, data } = await supabase.storage + .from('meme-videos') + .upload(filePath, formData.videoFile); + + if (uploadError) throw uploadError; + + // 2. Get the public URL + const { data: { publicUrl } } = supabase.storage + .from('meme-videos') + .getPublicUrl(filePath); + + // 3. Create template record + const { error: dbError } = await supabase + .from('meme_templates') + .insert({ + name: formData.name, + video_url: publicUrl, + instructions: formData.instructions, + }); + + if (dbError) throw dbError; + + // Reset form + setFormData({ + name: '', + instructions: '', + videoFile: null, + }); + + alert('Template uploaded successfully!'); + + } catch (error) { + console.error('Error:', error); + alert('Upload failed. Please try again.'); + } finally { + setUploading(false); + } + }; + + return ( +
+

Upload Meme Template

+ +
+
+ + setFormData(prev => ({ ...prev, name: e.target.value }))} + /> +
+ +
+ +