Compare commits
3 Commits
48f111a5c4
...
07289cd408
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07289cd408 | ||
|
|
4e580bae89 | ||
|
|
af4be8fddd |
@@ -1,10 +1,80 @@
|
||||
uvicorn[standard]
|
||||
librosa
|
||||
openai-whisper
|
||||
torch
|
||||
loguru
|
||||
fastapi
|
||||
python-multipart
|
||||
jinja2
|
||||
pillow
|
||||
python-dotenv
|
||||
annotated-types==0.7.0
|
||||
anyio==4.8.0
|
||||
audioread==3.0.1
|
||||
certifi==2025.1.31
|
||||
cffi==1.17.1
|
||||
charset-normalizer==3.4.1
|
||||
click==8.1.8
|
||||
decorator==5.2.1
|
||||
fastapi==0.115.11
|
||||
fastapi-mcp==0.2.0
|
||||
ffmpeg-python==0.2.0
|
||||
filelock==3.17.0
|
||||
fsspec==2025.3.0
|
||||
future==1.0.0
|
||||
h11==0.14.0
|
||||
httpcore==1.0.8
|
||||
httptools==0.6.4
|
||||
httpx==0.28.1
|
||||
httpx-sse==0.4.0
|
||||
huggingface-hub==0.29.2
|
||||
idna==3.10
|
||||
Jinja2==3.1.6
|
||||
joblib==1.4.2
|
||||
lazy_loader==0.4
|
||||
librosa==0.10.2.post1
|
||||
llvmlite==0.44.0
|
||||
loguru==0.7.3
|
||||
markdown-it-py==3.0.0
|
||||
MarkupSafe==3.0.2
|
||||
mcp==1.6.0
|
||||
mcp-proxy==0.5.1
|
||||
mdurl==0.1.2
|
||||
more-itertools==10.6.0
|
||||
mpmath==1.3.0
|
||||
msgpack==1.1.0
|
||||
networkx==3.4.2
|
||||
numba==0.61.0
|
||||
numpy==2.1.3
|
||||
packaging==24.2
|
||||
pillow==11.1.0
|
||||
platformdirs==4.3.6
|
||||
pooch==1.8.2
|
||||
pycparser==2.22
|
||||
pydantic==2.10.6
|
||||
pydantic-settings==2.8.1
|
||||
pydantic_core==2.27.2
|
||||
Pygments==2.19.1
|
||||
python-dotenv==1.0.1
|
||||
python-multipart==0.0.20
|
||||
PyYAML==6.0.2
|
||||
regex==2024.11.6
|
||||
requests==2.32.3
|
||||
rich==14.0.0
|
||||
safetensors==0.5.3
|
||||
scikit-learn==1.6.1
|
||||
scipy==1.15.2
|
||||
setuptools==75.8.0
|
||||
shellingham==1.5.4
|
||||
sniffio==1.3.1
|
||||
soundfile==0.13.1
|
||||
soxr==0.5.0.post1
|
||||
sse-starlette==2.2.1
|
||||
starlette==0.46.1
|
||||
sympy==1.13.1
|
||||
threadpoolctl==3.5.0
|
||||
tokenizers==0.21.0
|
||||
tomli==2.2.1
|
||||
torch==2.6.0
|
||||
tqdm==4.67.1
|
||||
transformers==4.49.0
|
||||
typer==0.15.2
|
||||
typing_extensions==4.12.2
|
||||
urllib3==2.3.0
|
||||
uvicorn==0.34.0
|
||||
uvloop==0.21.0
|
||||
watchfiles==1.0.4
|
||||
websockets==15.0.1
|
||||
wheel==0.45.1
|
||||
whisper-openai==1.0.0
|
||||
yt-dlp==2025.2.19
|
||||
|
||||
86
src/main.py
86
src/main.py
@@ -1,9 +1,11 @@
|
||||
import asyncio
|
||||
import os
|
||||
import shutil
|
||||
import json
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import List
|
||||
|
||||
from fastapi_mcp import FastApiMCP
|
||||
from fastapi import FastAPI, UploadFile, File, HTTPException, Request, WebSocket, WebSocketDisconnect, Form
|
||||
from fastapi.responses import HTMLResponse, JSONResponse, StreamingResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
@@ -29,8 +31,18 @@ async def lifespan(app: FastAPI):
|
||||
|
||||
|
||||
app = FastAPI(title="Transcriptor", lifespan=lifespan)
|
||||
mcp = FastApiMCP(app,
|
||||
name="agent-transcriptor",
|
||||
description="user uploads audio files (in mp3 format) and transcriptor uses AI STT model to transcribe text from files",
|
||||
base_url=os.environ["MCP_BASE_URL"],
|
||||
exclude_operations=["get_upload_page"],
|
||||
describe_all_responses=True,
|
||||
describe_full_response_schema=True,
|
||||
)
|
||||
|
||||
|
||||
active_connections = []
|
||||
mcp.mount()
|
||||
app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
|
||||
templates = Jinja2Templates(directory=TEMPLATES_DIR)
|
||||
|
||||
@@ -39,19 +51,23 @@ def is_valid_file(filename: str) -> bool:
|
||||
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
||||
|
||||
|
||||
@app.get("/", response_class=HTMLResponse)
|
||||
@app.get("/", response_class=HTMLResponse, operation_id="get_upload_page")
|
||||
async def get_upload_page(request: Request):
|
||||
"""Serve the HTML frontend"""
|
||||
return templates.TemplateResponse("index.html", {"request": request})
|
||||
|
||||
|
||||
@app.post("/upload")
|
||||
@app.post("/upload", operation_id="upload_single_audio_file")
|
||||
async def upload_file(file: UploadFile = File(...)):
|
||||
"""
|
||||
API endpoint to handle file uploads
|
||||
- Validates that the file is mp3 or wav
|
||||
- Saves it to the local filesystem
|
||||
- Checks for duplicate filenames
|
||||
Upload a single MP3/WAV audio file.
|
||||
|
||||
Example curl command:
|
||||
curl -X 'POST' \
|
||||
'http://0.0.0.0:33754/upload' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Content-Type: multipart/form-data' \
|
||||
-F 'file=@How To Parallel Charge LiPo Batteries Without Burning Down Your House.mp3;type=audio/mpeg'
|
||||
"""
|
||||
# Validate file extension
|
||||
if not is_valid_file(file.filename):
|
||||
@@ -75,12 +91,15 @@ async def upload_file(file: UploadFile = File(...)):
|
||||
return {"filename": file.filename, "saved_path": str(file_path), "status": "success"}
|
||||
|
||||
|
||||
@app.post("/upload-multiple")
|
||||
@app.post("/upload-multiple", operation_id="upload_multiple_audio_files")
|
||||
async def upload_multiple_files(files: List[UploadFile] = File(...)):
|
||||
"""
|
||||
API endpoint to handle multiple file uploads
|
||||
- Processes each file individually
|
||||
- Returns a summary of the upload results
|
||||
Upload multiple audio files simultaneously.
|
||||
|
||||
Parameters:
|
||||
- files: List of audio files (required)
|
||||
|
||||
Returns: Summary of upload results for each file
|
||||
"""
|
||||
results = []
|
||||
|
||||
@@ -137,10 +156,15 @@ async def upload_multiple_files(files: List[UploadFile] = File(...)):
|
||||
return summary
|
||||
|
||||
|
||||
@app.post("/process-multiple")
|
||||
@app.post("/process-multiple", operation_id="transcribe_multiple_files_in_batch")
|
||||
async def process_multiple_files(filenames: List[str] = Form(...)):
|
||||
"""
|
||||
API endpoint to process multiple files at once
|
||||
Batch transcribe multiple audio files by filename.
|
||||
|
||||
Parameters:
|
||||
- filenames: List of audio file names to process
|
||||
|
||||
Returns: Batch transcription results
|
||||
"""
|
||||
results = []
|
||||
|
||||
@@ -193,7 +217,12 @@ async def process_multiple_files(filenames: List[str] = Form(...)):
|
||||
|
||||
|
||||
def get_file_list():
|
||||
"""Helper function to get file list with metadata and status"""
|
||||
"""
|
||||
Retrieve audio files with metadata and processing status.
|
||||
|
||||
Returns: List of file objects containing name, size, creation time,
|
||||
processing status, and transcript availability
|
||||
"""
|
||||
files = []
|
||||
for file_path in UPLOAD_DIR.iterdir():
|
||||
if file_path.is_file() and file_path.name != '.gitkeep': # Skip .gitkeep file
|
||||
@@ -218,15 +247,28 @@ def get_file_list():
|
||||
return files
|
||||
|
||||
|
||||
@app.get("/files")
|
||||
@app.get("/files", operation_id="list_uploaded_files")
|
||||
async def list_files():
|
||||
"""API endpoint to list all uploaded files"""
|
||||
"""
|
||||
Retrieve list of all uploaded audio files with metadata.
|
||||
|
||||
Returns: Object containing files array with status information
|
||||
"""
|
||||
return {"files": get_file_list()}
|
||||
|
||||
|
||||
@app.post("/process/{filename}")
|
||||
@app.post("/process/{filename}", operation_id="transcribe_single_file")
|
||||
async def process_file(filename: str):
|
||||
"""API endpoint to manually trigger file processing"""
|
||||
"""
|
||||
Transcribe a single audio file by filename.
|
||||
|
||||
Parameters:
|
||||
- filename: Name of the audio file to process
|
||||
|
||||
Returns: Processing status information
|
||||
|
||||
Raises: 404 (file not found), 500 (processing failed)
|
||||
"""
|
||||
file_path = UPLOAD_DIR / filename
|
||||
|
||||
# Check if file exists
|
||||
@@ -244,10 +286,14 @@ async def process_file(filename: str):
|
||||
return {"filename": filename, "status": "processing_started"}
|
||||
|
||||
|
||||
@app.get("/download-transcripts")
|
||||
@app.get("/download-transcripts", operation_id="download_all_transcripts")
|
||||
async def download_transcripts():
|
||||
"""
|
||||
API endpoint to download all transcript files as a single ZIP
|
||||
Download all available transcripts as a ZIP archive.
|
||||
|
||||
Returns: ZIP file stream containing all transcript files
|
||||
|
||||
Raises: 404 (no transcripts available)
|
||||
"""
|
||||
# Check if there are any transcripts
|
||||
transcript_files = list(TRANSCRIPT_DIR.glob("*.txt"))
|
||||
@@ -307,4 +353,4 @@ async def broadcast_file_list():
|
||||
logger.error(f"Error broadcasting to a client: {e}")
|
||||
|
||||
|
||||
# Run with: uvicorn src.main:app --reload
|
||||
mcp.setup_server()
|
||||
|
||||
Reference in New Issue
Block a user