Ahmed Mostafa commited on
Commit
162cf59
·
1 Parent(s): 7f4c650
Files changed (6) hide show
  1. Dockerfile +28 -0
  2. README.md +10 -6
  3. app.py +5 -0
  4. packages.txt +1 -0
  5. requirements.txt +28 -0
  6. src/api/main.py +156 -0
Dockerfile ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use an official Python runtime as a parent image
2
+ FROM python:3.10-slim
3
+
4
+ # Set environment variables
5
+ ENV PYTHONDONTWRITEBYTECODE 1
6
+ ENV PYTHONUNBUFFERED 1
7
+
8
+ # Install system dependencies
9
+ RUN apt-get update && apt-get install -y \
10
+ ffmpeg \
11
+ git \
12
+ && rm -rf /var/lib/apt/lists/*
13
+
14
+ # Set work directory
15
+ WORKDIR /app
16
+
17
+ # Install Python dependencies
18
+ COPY requirements.txt .
19
+ RUN pip install --no-cache-dir -r requirements.txt
20
+
21
+ # Copy the rest of the application
22
+ COPY . .
23
+
24
+ # Hugging Face Spaces expects the app to run on port 7860
25
+ EXPOSE 7860
26
+
27
+ # Run the application
28
+ CMD ["python", "app.py"]
README.md CHANGED
@@ -1,12 +1,16 @@
1
  ---
2
- title: AIdea Server
3
- emoji: 😻
4
- colorFrom: yellow
5
  colorTo: purple
6
  sdk: docker
 
7
  pinned: false
8
- license: apache-2.0
9
- short_description: AIdea is app that simplifies watching videos by beautiful we
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
1
  ---
2
+ title: AIdea Note Generator
3
+ emoji: 📝
4
+ colorFrom: blue
5
  colorTo: purple
6
  sdk: docker
7
+ app_file: app.py
8
  pinned: false
 
 
9
  ---
10
 
11
+ # YouTube Study Notes AI
12
+
13
+ ![Python](https://img.shields.io/badge/python-3.8+-blue.svg)
14
+ ![License](https://img.shields.io/badge/license-MIT-green.svg)
15
+
16
+ An intelligent AI system that automatically generates structured study notes from YouTube educational videos using speech recognition and large language models.
app.py ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ import uvicorn
2
+ from src.api.main import app
3
+
4
+ if __name__ == "__main__":
5
+ uvicorn.run(app, host="0.0.0.0", port=7860)
packages.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ffmpeg
requirements.txt ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ yt-dlp==2024.12.23
2
+ pydub==0.25.1
3
+ openai-whisper==20250625
4
+ torch
5
+ torchaudio
6
+ google-genai
7
+ google-generativeai==0.3.2
8
+ langchain==0.1.0
9
+ langchain-google-genai==0.0.5
10
+ fastapi==0.109.0
11
+ uvicorn[standard]==0.27.0
12
+ pydantic==2.12.5
13
+ pydantic[email]
14
+ email-validator
15
+ pydantic-settings==2.1.0
16
+ python-multipart==0.0.6
17
+ python-dotenv==1.0.0
18
+ httpx==0.26.0
19
+ aiofiles==23.2.1
20
+ sqlmodel==0.0.14
21
+ asyncpg==0.31.0
22
+ greenlet==3.3.1
23
+ passlib[bcrypt]==1.7.4
24
+ python-jose[cryptography]==3.3.0
25
+ bcrypt==4.1.2
26
+ google-api-python-client==2.115.0
27
+ pydantic-core==2.41.5
28
+ ffmpeg-python
src/api/main.py ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import uuid
2
+ from datetime import datetime
3
+ from typing import Dict
4
+ from enum import Enum
5
+ from contextlib import asynccontextmanager
6
+
7
+ from fastapi import FastAPI, HTTPException, BackgroundTasks, Depends
8
+ from fastapi.middleware.cors import CORSMiddleware
9
+ from pydantic import BaseModel, HttpUrl
10
+
11
+ from src.ai_modules.transcription.audio_downloader import YouTubeDownloader
12
+ from src.ai_modules.transcription.whisper_transcriber import WhisperTranscriber
13
+ from src.ai_modules.summarization.note_generator import NoteGenerator
14
+ from src.utils.logger import setup_logger
15
+ from src.db.database import create_db_and_tables, async_engine
16
+ from src.db.models import Note, User
17
+ from src.auth.dependencies import get_current_user
18
+ from src.api.auth_routes import router as auth_router
19
+ from src.api.notes_routes import router as notes_router
20
+ from sqlmodel.ext.asyncio.session import AsyncSession
21
+
22
+ logger = setup_logger(__name__)
23
+
24
+
25
+ # --- Models ---
26
+ class TaskStatus(str, Enum):
27
+ PENDING = "pending"
28
+ DOWNLOADING = "downloading"
29
+ TRANSCRIBING = "transcribing"
30
+ GENERATING_NOTES = "generating_notes"
31
+ COMPLETED = "completed"
32
+ FAILED = "failed"
33
+
34
+
35
+ class GenerateNotesRequest(BaseModel):
36
+ youtube_url: HttpUrl
37
+ language: str = "en"
38
+
39
+
40
+ class TaskResponse(BaseModel):
41
+ task_id: str
42
+ status: TaskStatus
43
+ message: str
44
+
45
+
46
+ # Global task storage
47
+ tasks: Dict[str, Dict] = {}
48
+
49
+
50
+ @asynccontextmanager
51
+ async def lifespan(app: FastAPI):
52
+ logger.info("Lifespan: Initializing database tables...")
53
+ await create_db_and_tables()
54
+ yield
55
+
56
+
57
+ app = FastAPI(title="YouTube Study Notes AI", lifespan=lifespan)
58
+
59
+ app.add_middleware(
60
+ CORSMiddleware,
61
+ allow_origins=["*"],
62
+ allow_credentials=True,
63
+ allow_methods=["*"],
64
+ allow_headers=["*"],
65
+ )
66
+
67
+ # --- Routes ---
68
+ app.include_router(auth_router)
69
+ app.include_router(notes_router)
70
+
71
+
72
+ @app.post("/generate", response_model=TaskResponse)
73
+ async def generate(
74
+ request: GenerateNotesRequest,
75
+ background_tasks: BackgroundTasks,
76
+ current_user: User = Depends(get_current_user),
77
+ ):
78
+ task_id = str(uuid.uuid4())
79
+ user_id = current_user.id
80
+
81
+ tasks[task_id] = {
82
+ "status": TaskStatus.PENDING,
83
+ "message": "Initializing...",
84
+ "youtube_url": str(request.youtube_url),
85
+ "user_id": user_id,
86
+ "created_at": datetime.now(),
87
+ }
88
+
89
+ background_tasks.add_task(
90
+ process_video_and_save,
91
+ task_id,
92
+ str(request.youtube_url),
93
+ request.language,
94
+ user_id,
95
+ )
96
+
97
+ return TaskResponse(
98
+ task_id=task_id,
99
+ status=TaskStatus.PENDING,
100
+ message="Generation started successfully.",
101
+ )
102
+
103
+
104
+ async def process_video_and_save(
105
+ task_id: str, youtube_url: str, language: str, user_id: int
106
+ ):
107
+ audio_file = None
108
+ try:
109
+ tasks[task_id]["status"] = TaskStatus.DOWNLOADING
110
+ downloader = YouTubeDownloader()
111
+ video_info = downloader.get_video_info(youtube_url)
112
+ audio_file = downloader.download_audio(youtube_url, task_id)
113
+
114
+ tasks[task_id]["status"] = TaskStatus.TRANSCRIBING
115
+ transcriber = WhisperTranscriber()
116
+ transcript_data = transcriber.transcribe(audio_file, language=language)
117
+
118
+ tasks[task_id]["status"] = TaskStatus.GENERATING_NOTES
119
+ note_gen = NoteGenerator()
120
+ json_notes = note_gen.generate_notes_json(
121
+ transcript_data["text"], video_info["title"]
122
+ )
123
+ final_notes = note_gen.format_final_notes(
124
+ note_gen.format_notes_to_markdown(json_notes),
125
+ video_info["title"],
126
+ youtube_url,
127
+ video_info["duration"],
128
+ )
129
+
130
+ async with AsyncSession(async_engine) as session:
131
+ new_note = Note(
132
+ user_id=user_id,
133
+ video_url=youtube_url,
134
+ video_title=video_info["title"],
135
+ summary_content=final_notes,
136
+ )
137
+ session.add(new_note)
138
+ await session.commit()
139
+ await session.refresh(new_note)
140
+
141
+ tasks[task_id]["notes"] = final_notes
142
+ tasks[task_id]["keyPoints"] = json_notes.get("key_points", []) if isinstance(json_notes, dict) else []
143
+ tasks[task_id]["status"] = TaskStatus.COMPLETED
144
+ except Exception as e:
145
+ logger.error(f"Task failed: {e}")
146
+ tasks[task_id]["status"] = TaskStatus.FAILED
147
+ finally:
148
+ if audio_file and audio_file.exists():
149
+ downloader.cleanup(audio_file)
150
+
151
+
152
+ @app.get("/status/{task_id}")
153
+ async def get_task_status(task_id: str):
154
+ if task_id not in tasks:
155
+ raise HTTPException(status_code=404, detail="Task not found")
156
+ return tasks[task_id]