Spaces:
Running
Running
Ved Gupta commited on
Commit ·
022e710
1
Parent(s): 8f082c1
Updated v3
Browse files- app/api/endpoints/transcribe.py +19 -5
- app/core/models/AuthToken.py +28 -2
- app/core/models/Transcribe.py +25 -1
- app/core/models/User.py +8 -2
- app/core/models/__init__.py +2 -2
- app/utils/utils.py +27 -5
- binary/whisper +0 -0
app/api/endpoints/transcribe.py
CHANGED
|
@@ -1,11 +1,17 @@
|
|
| 1 |
from typing import Annotated, List, Union
|
| 2 |
|
| 3 |
from fastapi import APIRouter, File, UploadFile, Request, Header, HTTPException
|
|
|
|
| 4 |
from pydantic import BaseModel
|
| 5 |
|
| 6 |
-
from app.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
router = APIRouter()
|
|
|
|
| 9 |
|
| 10 |
|
| 11 |
class Transcription(BaseModel):
|
|
@@ -15,16 +21,24 @@ class Transcription(BaseModel):
|
|
| 15 |
|
| 16 |
@router.post("/", response_model=Transcription)
|
| 17 |
async def post_audio(
|
|
|
|
| 18 |
request: Request,
|
| 19 |
file: UploadFile = File(...),
|
| 20 |
Authentication: Annotated[Union[str, None], Header()] = None,
|
| 21 |
):
|
| 22 |
-
print(f"Authorization header: {Authentication}")
|
| 23 |
-
|
| 24 |
try:
|
| 25 |
-
|
| 26 |
file_path = save_audio_file(file)
|
| 27 |
-
data =
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
return Transcription(filename=file.filename, text=data)
|
| 29 |
except Exception as e:
|
| 30 |
raise HTTPException(status_code=400, detail=e.__str__())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
from typing import Annotated, List, Union
|
| 2 |
|
| 3 |
from fastapi import APIRouter, File, UploadFile, Request, Header, HTTPException
|
| 4 |
+
from fastapi.background import BackgroundTasks
|
| 5 |
from pydantic import BaseModel
|
| 6 |
|
| 7 |
+
from app.core.database import SessionLocal
|
| 8 |
+
|
| 9 |
+
from app.utils.utils import save_audio_file, transcribe_file, get_audio_duration
|
| 10 |
+
from app.core.models import AuthTokenController, TranscribeController
|
| 11 |
+
|
| 12 |
|
| 13 |
router = APIRouter()
|
| 14 |
+
database = SessionLocal()
|
| 15 |
|
| 16 |
|
| 17 |
class Transcription(BaseModel):
|
|
|
|
| 21 |
|
| 22 |
@router.post("/", response_model=Transcription)
|
| 23 |
async def post_audio(
|
| 24 |
+
background_tasks: BackgroundTasks,
|
| 25 |
request: Request,
|
| 26 |
file: UploadFile = File(...),
|
| 27 |
Authentication: Annotated[Union[str, None], Header()] = None,
|
| 28 |
):
|
|
|
|
|
|
|
| 29 |
try:
|
| 30 |
+
userId = AuthTokenController(database).get_userid_from_token(Authentication)
|
| 31 |
file_path = save_audio_file(file)
|
| 32 |
+
[data, output_audio_path] = transcribe_file(file_path)
|
| 33 |
+
background_tasks.add_task(
|
| 34 |
+
create_transcribe_record, database, userId, data, output_audio_path
|
| 35 |
+
)
|
| 36 |
+
|
| 37 |
return Transcription(filename=file.filename, text=data)
|
| 38 |
except Exception as e:
|
| 39 |
raise HTTPException(status_code=400, detail=e.__str__())
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def create_transcribe_record(database, userId, data, file_path):
|
| 43 |
+
duration = get_audio_duration(file_path)
|
| 44 |
+
TranscribeController(database).create(user_id=userId, text=data, duration=duration)
|
app/core/models/AuthToken.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
| 1 |
-
from datetime import datetime, timedelta
|
| 2 |
import uuid
|
| 3 |
import random
|
| 4 |
import string
|
|
@@ -7,6 +6,8 @@ from app.core.config import settings
|
|
| 7 |
|
| 8 |
from sqlalchemy.dialects.postgresql import UUID
|
| 9 |
from sqlalchemy import Column, String, Boolean, ForeignKey, DateTime
|
|
|
|
|
|
|
| 10 |
from sqlalchemy.sql import func
|
| 11 |
|
| 12 |
from app.core.database import Base
|
|
@@ -19,10 +20,35 @@ class AuthToken(Base):
|
|
| 19 |
token = Column(String, unique=True, index=True)
|
| 20 |
user_id = Column(UUID(as_uuid=True), ForeignKey("users.id"))
|
| 21 |
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
|
| 23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
self.user_id = user_id
|
| 25 |
self.token = self.create_token()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
|
| 27 |
def create_token(self):
|
| 28 |
token = self.generate_bearer_token()
|
|
|
|
|
|
|
| 1 |
import uuid
|
| 2 |
import random
|
| 3 |
import string
|
|
|
|
| 6 |
|
| 7 |
from sqlalchemy.dialects.postgresql import UUID
|
| 8 |
from sqlalchemy import Column, String, Boolean, ForeignKey, DateTime
|
| 9 |
+
from sqlalchemy.orm import relationship
|
| 10 |
+
|
| 11 |
from sqlalchemy.sql import func
|
| 12 |
|
| 13 |
from app.core.database import Base
|
|
|
|
| 20 |
token = Column(String, unique=True, index=True)
|
| 21 |
user_id = Column(UUID(as_uuid=True), ForeignKey("users.id"))
|
| 22 |
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
| 23 |
+
user = relationship("UserInDB", back_populates="auth_tokens")
|
| 24 |
+
|
| 25 |
+
def __init__(self, user_id: UUID, token: str):
|
| 26 |
+
self.user_id = user_id
|
| 27 |
+
self.token = token
|
| 28 |
+
|
| 29 |
|
| 30 |
+
class AuthTokenController:
|
| 31 |
+
AuthToken = AuthToken
|
| 32 |
+
|
| 33 |
+
def __init__(self, database):
|
| 34 |
+
self.db = database
|
| 35 |
+
|
| 36 |
+
def get_userid_from_token(self, token) -> str:
|
| 37 |
+
user = self.db.query(AuthToken).filter(AuthToken.token == token).first()
|
| 38 |
+
if not user:
|
| 39 |
+
raise Exception("Invalid Token!")
|
| 40 |
+
return user.user_id
|
| 41 |
+
|
| 42 |
+
def create(self, user_id: UUID):
|
| 43 |
self.user_id = user_id
|
| 44 |
self.token = self.create_token()
|
| 45 |
+
self.auth_token = AuthToken(self.user_id, self.token)
|
| 46 |
+
self.db.add(self.auth_token)
|
| 47 |
+
self.db.commit()
|
| 48 |
+
self.db.refresh(self.auth_token)
|
| 49 |
+
|
| 50 |
+
def get_token(self):
|
| 51 |
+
return self.auth_token.token
|
| 52 |
|
| 53 |
def create_token(self):
|
| 54 |
token = self.generate_bearer_token()
|
app/core/models/Transcribe.py
CHANGED
|
@@ -4,7 +4,7 @@ import uuid
|
|
| 4 |
from app.core.config import settings
|
| 5 |
|
| 6 |
from sqlalchemy.dialects.postgresql import UUID
|
| 7 |
-
from sqlalchemy import Column, String, Boolean, ForeignKey, DateTime
|
| 8 |
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
| 9 |
from sqlalchemy.sql import func
|
| 10 |
|
|
@@ -16,6 +16,30 @@ class TranscibeInDB(Base):
|
|
| 16 |
|
| 17 |
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
| 18 |
text = Column(String)
|
|
|
|
| 19 |
user_id = Column(UUID(as_uuid=True), ForeignKey("users.id"))
|
| 20 |
user = relationship("UserInDB", back_populates="transcribes")
|
| 21 |
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
from app.core.config import settings
|
| 5 |
|
| 6 |
from sqlalchemy.dialects.postgresql import UUID
|
| 7 |
+
from sqlalchemy import Column, String, Boolean, ForeignKey, DateTime, Integer
|
| 8 |
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
| 9 |
from sqlalchemy.sql import func
|
| 10 |
|
|
|
|
| 16 |
|
| 17 |
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
| 18 |
text = Column(String)
|
| 19 |
+
audio_duration = Column(Integer)
|
| 20 |
user_id = Column(UUID(as_uuid=True), ForeignKey("users.id"))
|
| 21 |
user = relationship("UserInDB", back_populates="transcribes")
|
| 22 |
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
| 23 |
+
|
| 24 |
+
def __init__(self, user_id, text, audio_duration):
|
| 25 |
+
self.text = text
|
| 26 |
+
self.audio_duration = audio_duration
|
| 27 |
+
self.user_id = user_id
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
class TranscribeController:
|
| 31 |
+
TranscibeInDB = TranscibeInDB
|
| 32 |
+
|
| 33 |
+
def __init__(self, database):
|
| 34 |
+
self.db = database
|
| 35 |
+
|
| 36 |
+
def create(self, user_id: UUID, text: str, duration: int):
|
| 37 |
+
self.user_id = user_id
|
| 38 |
+
self.text = text
|
| 39 |
+
self.audio_duration = duration
|
| 40 |
+
self.transcribe_data = TranscibeInDB(
|
| 41 |
+
user_id=self.user_id, text=self.text, audio_duration=self.audio_duration
|
| 42 |
+
)
|
| 43 |
+
self.db.add(self.transcribe_data)
|
| 44 |
+
self.db.commit()
|
| 45 |
+
self.db.refresh(self.transcribe_data)
|
app/core/models/User.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
| 1 |
import uuid
|
| 2 |
|
| 3 |
-
|
| 4 |
from app.core.config import settings
|
| 5 |
|
| 6 |
from sqlalchemy.dialects.postgresql import UUID
|
|
@@ -12,6 +11,8 @@ from app.core.database import Base
|
|
| 12 |
|
| 13 |
from app.core.security import get_password_hash, verify_password
|
| 14 |
|
|
|
|
|
|
|
| 15 |
|
| 16 |
class UserInDB(Base):
|
| 17 |
__tablename__ = "users"
|
|
@@ -22,6 +23,7 @@ class UserInDB(Base):
|
|
| 22 |
hashed_password = Column(String)
|
| 23 |
is_active = Column(Boolean, default=True)
|
| 24 |
transcribes = relationship("TranscibeInDB", back_populates="user")
|
|
|
|
| 25 |
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
| 26 |
|
| 27 |
def __init__(self, username: str, email: str, hashed_password: str):
|
|
@@ -44,7 +46,7 @@ class UserController:
|
|
| 44 |
def __init__(self, database):
|
| 45 |
self.db = database
|
| 46 |
|
| 47 |
-
def create(self, username: str, email: str, password: str):
|
| 48 |
isUserExists: Boolean = self.CheckUserIsExistsByEmailAndUsername(
|
| 49 |
email, username
|
| 50 |
)
|
|
@@ -65,6 +67,10 @@ class UserController:
|
|
| 65 |
self.db.refresh(self.db_user)
|
| 66 |
self.user = self.db_user.data()
|
| 67 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
def CheckUserIsExistsByEmailAndUsername(self, email: str, username: str):
|
| 69 |
db_user = (
|
| 70 |
self.db.query(UserInDB)
|
|
|
|
| 1 |
import uuid
|
| 2 |
|
|
|
|
| 3 |
from app.core.config import settings
|
| 4 |
|
| 5 |
from sqlalchemy.dialects.postgresql import UUID
|
|
|
|
| 11 |
|
| 12 |
from app.core.security import get_password_hash, verify_password
|
| 13 |
|
| 14 |
+
from app.core.models import AuthTokenController
|
| 15 |
+
|
| 16 |
|
| 17 |
class UserInDB(Base):
|
| 18 |
__tablename__ = "users"
|
|
|
|
| 23 |
hashed_password = Column(String)
|
| 24 |
is_active = Column(Boolean, default=True)
|
| 25 |
transcribes = relationship("TranscibeInDB", back_populates="user")
|
| 26 |
+
auth_tokens = relationship("AuthToken", back_populates="user")
|
| 27 |
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
| 28 |
|
| 29 |
def __init__(self, username: str, email: str, hashed_password: str):
|
|
|
|
| 46 |
def __init__(self, database):
|
| 47 |
self.db = database
|
| 48 |
|
| 49 |
+
def create(self, username: str, email: str, password: str, init_token: bool = True):
|
| 50 |
isUserExists: Boolean = self.CheckUserIsExistsByEmailAndUsername(
|
| 51 |
email, username
|
| 52 |
)
|
|
|
|
| 67 |
self.db.refresh(self.db_user)
|
| 68 |
self.user = self.db_user.data()
|
| 69 |
|
| 70 |
+
if init_token == False:
|
| 71 |
+
return
|
| 72 |
+
AuthTokenController(self.db).create(self.db_user.id)
|
| 73 |
+
|
| 74 |
def CheckUserIsExistsByEmailAndUsername(self, email: str, username: str):
|
| 75 |
db_user = (
|
| 76 |
self.db.query(UserInDB)
|
app/core/models/__init__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
-
from .AuthToken import AuthToken
|
| 2 |
from .User import UserInDB
|
| 3 |
-
from .Transcribe import TranscibeInDB
|
| 4 |
|
| 5 |
from app.core.database import Base, engine
|
| 6 |
|
|
|
|
| 1 |
+
from .AuthToken import AuthToken, AuthTokenController
|
| 2 |
from .User import UserInDB
|
| 3 |
+
from .Transcribe import TranscibeInDB, TranscribeController
|
| 4 |
|
| 5 |
from app.core.database import Base, engine
|
| 6 |
|
app/utils/utils.py
CHANGED
|
@@ -2,6 +2,7 @@ import json
|
|
| 2 |
import subprocess
|
| 3 |
import uuid
|
| 4 |
import logging
|
|
|
|
| 5 |
|
| 6 |
|
| 7 |
def get_all_routes(app):
|
|
@@ -33,18 +34,20 @@ def print_routes(app):
|
|
| 33 |
print("\n")
|
| 34 |
|
| 35 |
|
| 36 |
-
def
|
| 37 |
-
"""./binary/whisper -m models/ggml-tiny.en.bin -f Rev.mp3 -nt --output-text out1.txt"""
|
| 38 |
try:
|
| 39 |
if path is None:
|
| 40 |
raise Exception("No path provided")
|
| 41 |
-
|
| 42 |
-
|
|
|
|
|
|
|
| 43 |
execute_command(command)
|
| 44 |
f = open(outputFilePath, "r")
|
| 45 |
data = f.read()
|
| 46 |
f.close()
|
| 47 |
-
return data
|
| 48 |
except Exception as e:
|
| 49 |
logging.error(e)
|
| 50 |
raise Exception(e.__str__())
|
|
@@ -66,3 +69,22 @@ def save_audio_file(file=None):
|
|
| 66 |
with open(path, "wb") as f:
|
| 67 |
f.write(file.file.read())
|
| 68 |
return path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
import subprocess
|
| 3 |
import uuid
|
| 4 |
import logging
|
| 5 |
+
import wave
|
| 6 |
|
| 7 |
|
| 8 |
def get_all_routes(app):
|
|
|
|
| 34 |
print("\n")
|
| 35 |
|
| 36 |
|
| 37 |
+
def transcribe_file(path: str = None, model="ggml-model-whisper-tiny.en-q5_1.bin"):
|
| 38 |
+
"""./binary/whisper -m models/ggml-tiny.en.bin -f Rev.mp3 out.wav -nt --output-text out1.txt"""
|
| 39 |
try:
|
| 40 |
if path is None:
|
| 41 |
raise Exception("No path provided")
|
| 42 |
+
rand = uuid.uuid4()
|
| 43 |
+
outputFilePath: str = f"transcribe/{rand}.txt"
|
| 44 |
+
output_audio_path: str = f"audio/{rand}.wav"
|
| 45 |
+
command: str = f"./binary/whisper -m models/{model} -f {path} {output_audio_path} -nt --output-text {outputFilePath}"
|
| 46 |
execute_command(command)
|
| 47 |
f = open(outputFilePath, "r")
|
| 48 |
data = f.read()
|
| 49 |
f.close()
|
| 50 |
+
return [data, output_audio_path]
|
| 51 |
except Exception as e:
|
| 52 |
logging.error(e)
|
| 53 |
raise Exception(e.__str__())
|
|
|
|
| 69 |
with open(path, "wb") as f:
|
| 70 |
f.write(file.file.read())
|
| 71 |
return path
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
def get_audio_duration(audio_file):
|
| 75 |
+
"""Gets the duration of the audio file in seconds.
|
| 76 |
+
|
| 77 |
+
Args:
|
| 78 |
+
audio_file: The path to the audio file.
|
| 79 |
+
|
| 80 |
+
Returns:
|
| 81 |
+
The duration of the audio file in seconds.
|
| 82 |
+
"""
|
| 83 |
+
|
| 84 |
+
with wave.open(audio_file, "rb") as f:
|
| 85 |
+
frames = f.getnframes()
|
| 86 |
+
sample_rate = f.getframerate()
|
| 87 |
+
duration = frames / sample_rate
|
| 88 |
+
rounded_duration = int(round(duration, 0))
|
| 89 |
+
|
| 90 |
+
return rounded_duration
|
binary/whisper
CHANGED
|
Binary files a/binary/whisper and b/binary/whisper differ
|
|
|