diff --git a/package.json b/package.json index a7fd19e..eeaf140 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,10 @@ "hoyahax-2025": "file:", "lucide-react": "^0.474.0", "mongoose": "^8.9.5", + "multer": "^1.4.5-lts.1", "next": "15.1.6", "next-themes": "^0.4.4", + "openai-whisper": "^1.0.2", "react": "^19.0.0", "react-dom": "^19.0.0", "svix": "^1.45.1", @@ -35,6 +37,7 @@ }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", + "@types/multer": "^1.4.12", "@types/node": "^20.17.16", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", diff --git a/src/app/api/transcribe/__pycache__/app.cpython-39.pyc b/src/app/api/transcribe/__pycache__/app.cpython-39.pyc new file mode 100644 index 0000000..423ad4b Binary files /dev/null and b/src/app/api/transcribe/__pycache__/app.cpython-39.pyc differ diff --git a/src/app/api/transcribe/app.py b/src/app/api/transcribe/app.py new file mode 100644 index 0000000..12f6408 --- /dev/null +++ b/src/app/api/transcribe/app.py @@ -0,0 +1,34 @@ +from fastapi import FastAPI, File, UploadFile +from fastapi.middleware.cors import CORSMiddleware +import whisper +import os +import tempfile + +app = FastAPI() +model = whisper.load_model("base") # Load the model once for efficiency + +app.add_middleware( + CORSMiddleware, + allow_origins=["http://localhost:3000"], # Frontend origin (adjust as needed) + allow_credentials=True, + allow_methods=["*"], # Allow all HTTP methods (GET, POST, etc.) + allow_headers=["*"], # Allow all headers (Authorization, Content-Type, etc.) +) + +@app.post("/transcribe") +async def transcribe_audio(file: UploadFile = File(...)): + # Save the uploaded file to a temporary location + with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as temp_file: + temp_file.write(await file.read()) + temp_path = temp_file.name + + try: + # Transcribe the audio + result = model.transcribe("inputs/test.mp3") + transcription = result["text"] + print(transcription) + finally: + # Clean up temporary file + os.remove(temp_path) + + return {"transcription": transcription} diff --git a/src/app/api/transcribe/inputs/test.mp3 b/src/app/api/transcribe/inputs/test.mp3 new file mode 100644 index 0000000..d5648ee Binary files /dev/null and b/src/app/api/transcribe/inputs/test.mp3 differ diff --git a/src/app/api/transcribe/route.ts b/src/app/api/transcribe/route.ts new file mode 100644 index 0000000..ce4efac --- /dev/null +++ b/src/app/api/transcribe/route.ts @@ -0,0 +1,55 @@ +import { NextRequest, NextResponse } from "next/server"; +import multer from "multer"; +import fs from "fs/promises"; +import whisper from "openai-whisper"; + +const upload = multer({ dest: "uploads/" }); + +export const config = { + api: { + bodyParser: false, // Disable Next.js's body parsing for file uploads + }, +}; + +// Whisper model (initialize once for efficiency) +const model = whisper.load_model("base"); + +// Utility to transcribe audio +async function transcribeAudio(filePath: string): Promise { + const transcription = await model.transcribe(filePath); + return transcription.text; +} + +async function parseMultipartForm(req: NextRequest): Promise { + return new Promise((resolve, reject) => { + const multerMiddleware = upload.single("audio"); + multerMiddleware(req as any, {} as any, (error: any) => { + if (error) return reject(error); + resolve(req.file); + }); + }); +} + +export async function POST(req: NextRequest) { + try { + // Parse the incoming multipart form data + const file = await parseMultipartForm(req); + + if (!file) { + return NextResponse.json({ error: "No audio file provided" }, { status: 400 }); + } + + const filePath = file.path; + + // Transcribe the audio + const transcription = await transcribeAudio(filePath); + + // Clean up the uploaded file + await fs.unlink(filePath); + + return NextResponse.json({ transcription }); + } catch (error) { + console.error("Error during transcription:", error); + return NextResponse.json({ error: "Transcription failed" }, { status: 500 }); + } +} diff --git a/src/app/page.tsx b/src/app/page.tsx new file mode 100644 index 0000000..29439a8 --- /dev/null +++ b/src/app/page.tsx @@ -0,0 +1,17 @@ +import RootLayout from './layout' +import SignupPage from './signup/page' +import Link from "next/link"; + +export default function Home() { + return ( + +
+

Welcome to Hoya

+ + + + +
+
+ ) +} diff --git a/src/app/transcribe/layout.tsx b/src/app/transcribe/layout.tsx new file mode 100644 index 0000000..a14e64f --- /dev/null +++ b/src/app/transcribe/layout.tsx @@ -0,0 +1,16 @@ +export const metadata = { + title: 'Next.js', + description: 'Generated by Next.js', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} diff --git a/src/app/transcribe/page.tsx b/src/app/transcribe/page.tsx new file mode 100644 index 0000000..8ff8e61 --- /dev/null +++ b/src/app/transcribe/page.tsx @@ -0,0 +1,56 @@ +"use client"; + +import React, { useState } from "react"; +import axios from "axios"; + +const AudioTranscriber: React.FC = () => { + const [file, setFile] = useState(null); + const [transcription, setTranscription] = useState(null); + const [loading, setLoading] = useState(false); + + const handleFileChange = (event: React.ChangeEvent) => { + if (event.target.files && event.target.files.length > 0) { + setFile(event.target.files[0]); + } + }; + + const handleTranscription = async () => { + if (!file) return alert("Please select an audio file to transcribe!"); + + const formData = new FormData(); + formData.append("file", file); + + setLoading(true); + try { + const response = await axios.post("http://localhost:8000/transcribe", formData, { + headers: { + "Content-Type": "multipart/form-data", + }, + }); + setTranscription(response.data.transcription); + } catch (error) { + console.error("Error transcribing audio:", error); + alert("Failed to transcribe audio. Please try again."); + } finally { + setLoading(false); + } + }; + + return ( +
+

Audio Transcription

+ + + {transcription && ( +
+

Transcription:

+

{transcription}

+
+ )} +
+ ); +}; + +export default AudioTranscriber; diff --git a/src/components/navbar.tsx b/src/components/navbar.tsx index 204e3be..74e18d7 100644 --- a/src/components/navbar.tsx +++ b/src/components/navbar.tsx @@ -28,6 +28,9 @@ export function Navbar() { Map + + Transcribe +