Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ Online learning has taken the front seat in the post-pandemic age. With the adve

EduAid is one such project currently available in the form of a browser extension.

## System Requirements

- **ffmpeg** or **libav** installed and available on your system `PATH`. Required for transcoding audio formats such as `.mp3` to `.wav`. (Note: `.wav` files are supported directly by the backend and do not require conversion).
- The Python dependencies **pydub** and **SpeechRecognition** must be installed (these are automatically installed via `pip install -r requirements.txt`).

## Installation and Setup

### 1. Clone the Repository
Expand Down
83 changes: 73 additions & 10 deletions backend/Generator/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
import os
import fitz
import mammoth
import speech_recognition as sr
from pydub import AudioSegment
import uuid

class MCQGenerator:

Expand Down Expand Up @@ -367,20 +370,80 @@ def extract_text_from_docx(self, file_path):
result = mammoth.extract_raw_text(docx_file)
return result.value

def extract_text_from_audio(self, file_path):
wav_path = file_path
request_id = uuid.uuid4().hex
chunk_files = []
try:
if file_path.endswith('.mp3'):
audio = AudioSegment.from_file(file_path, format='mp3')
wav_path = os.path.join(self.upload_folder, f"{request_id}.wav")
audio.export(wav_path, format='wav')

r = sr.Recognizer()
r.operation_timeout = 20 # Fail fast if speech recognition hangs
audio = AudioSegment.from_wav(wav_path)

# Enforce max duration of 10 minutes (600,000 milliseconds)
max_duration_ms = 10 * 60 * 1000
if len(audio) > max_duration_ms:
raise ValueError("Audio file is too long. Maximum supported duration is 10 minutes.")

chunk_length_ms = 60 * 1000
audio_len = len(audio)

full_text = []
for i, start_ms in enumerate(range(0, audio_len, chunk_length_ms)):
chunk = audio[start_ms : start_ms + chunk_length_ms]
chunk_filename = os.path.join(self.upload_folder, f"{request_id}_chunk_{i}.wav")
chunk_files.append(chunk_filename)
chunk.export(chunk_filename, format='wav')

with sr.AudioFile(chunk_filename) as source:
audio_data = r.record(source)
try:
text = r.recognize_google(audio_data)
full_text.append(text)
except sr.UnknownValueError:
full_text.append("[Unintelligible]")
except sr.RequestError as e:
raise RuntimeError("Could not request results") from e
Comment thread
coderabbitai[bot] marked this conversation as resolved.
finally:
for chunk_file in chunk_files:
if os.path.exists(chunk_file):
os.remove(chunk_file)
if wav_path != file_path and os.path.exists(wav_path):
os.remove(wav_path)

return "\n".join(full_text)

def process_file(self, file):
file_path = os.path.join(self.upload_folder, file.filename)
file.save(file_path)
# Extract and validate extension
_, ext = os.path.splitext(file.filename)
ext = ext.lower()
if ext not in ['.txt', '.pdf', '.docx', '.wav', '.mp3']:
raise ValueError('Unsupported file format')

# Generate safe storage name to prevent path traversal & collisions
temp_filename = f"{uuid.uuid4().hex}{ext}"
file_path = os.path.join(self.upload_folder, temp_filename)
content = ""

if file.filename.endswith('.txt'):
with open(file_path, 'r') as f:
content = f.read()
elif file.filename.endswith('.pdf'):
content = self.extract_text_from_pdf(file_path)
elif file.filename.endswith('.docx'):
content = self.extract_text_from_docx(file_path)
try:
file.save(file_path)
if ext == '.txt':
with open(file_path, 'r') as f:
content = f.read()
elif ext == '.pdf':
content = self.extract_text_from_pdf(file_path)
elif ext == '.docx':
content = self.extract_text_from_docx(file_path)
elif ext in ['.wav', '.mp3']:
content = self.extract_text_from_audio(file_path)
finally:
if os.path.exists(file_path):
os.remove(file_path)
Comment thread
coderabbitai[bot] marked this conversation as resolved.

os.remove(file_path)
return content

class QuestionGenerator:
Expand Down
7 changes: 6 additions & 1 deletion backend/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,12 @@ def upload_file():
if file.filename == '':
return jsonify({"error": "No selected file"}), 400

content = file_processor.process_file(file)
try:
content = file_processor.process_file(file)
except ValueError as e:
return jsonify({"error": str(e)}), 400
except RuntimeError as e:
return jsonify({"error": str(e)}), 503

if content:
return jsonify({"content": content})
Expand Down
4 changes: 2 additions & 2 deletions eduaid_web/src/pages/Text_Input.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,9 @@ const Text_Input = () => {
{/* File Upload Section */}
<div className="w-full max-w-2xl mx-auto border-[3px] rounded-2xl text-center px-6 py-6 border-dotted border-[#3E5063] mt-6">
<img className="mx-auto mb-2" height={32} width={32} src={cloud} alt="cloud" />
<p className="text-white text-lg">Choose a file (PDF, MP3 supported)</p>
<p className="text-white text-lg">Choose a file (PDF, TXT, DOCX, Audio supported)</p>

<input type="file" ref={fileInputRef} onChange={handleFileUpload} style={{ display: "none" }} />
<input type="file" accept=".txt,.pdf,.docx,.mp3,.wav" ref={fileInputRef} onChange={handleFileUpload} style={{ display: "none" }} />
<button
className="bg-[#3e506380] my-4 text-lg rounded-2xl text-white border border-[#cbd0dc80] px-6 py-2"
onClick={handleClick}
Expand Down
3 changes: 2 additions & 1 deletion extension/src/pages/text_input/TextInput.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -234,11 +234,12 @@ function Second() {
<img className="mx-auto" height={24} width={24} src={cloud} alt="cloud" />
<div className="text-center text-white text-sm">Choose a file</div>
<div className="text-center text-white text-sm">
PDF, MP3 supported
PDF, TXT, DOCX, Audio supported
</div>
<div>
<input
type="file"
accept=".txt,.pdf,.docx,.mp3,.wav"
ref={fileInputRef}
onChange={handleFileUpload}
style={{ display: 'none' }}
Expand Down
4 changes: 3 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,7 @@ tokenizers
mammoth
mediawikiapi
PyMuPDF
textblob
textblob==0.19.0
SpeechRecognition==3.14.5
pydub==0.25.1
llama-cpp-python