Commit
·
c564fad
1
Parent(s):
25a7124
make api stateless with tempfile.
Browse files- Dockerfile +5 -1
- main.py +38 -46
- utils/archiver.py +0 -17
- utils/process_video.py +15 -25
- utils/transcriber.py +5 -5
- utils/zip_response.py +5 -5
Dockerfile
CHANGED
@@ -1,11 +1,15 @@
|
|
1 |
# Use an official Python runtime as a parent image
|
2 |
FROM python:3.11.7-slim-bullseye
|
3 |
|
|
|
|
|
|
|
|
|
4 |
# Set the working directory in the container to /app
|
5 |
WORKDIR /app
|
6 |
|
7 |
# Copy the current directory contents into the container at /app
|
8 |
-
COPY . /app
|
9 |
|
10 |
#Install ImageMagick
|
11 |
RUN apt-get update && apt-get install -y imagemagick && sed -i '91d' /etc/ImageMagick-6/policy.xml
|
|
|
1 |
# Use an official Python runtime as a parent image
|
2 |
FROM python:3.11.7-slim-bullseye
|
3 |
|
4 |
+
RUN useradd -m -u 1000 user
|
5 |
+
USER user
|
6 |
+
ENV PATH="/home/user/.local/bin:$PATH"
|
7 |
+
|
8 |
# Set the working directory in the container to /app
|
9 |
WORKDIR /app
|
10 |
|
11 |
# Copy the current directory contents into the container at /app
|
12 |
+
COPY --chown=user . /app
|
13 |
|
14 |
#Install ImageMagick
|
15 |
RUN apt-get update && apt-get install -y imagemagick && sed -i '91d' /etc/ImageMagick-6/policy.xml
|
main.py
CHANGED
@@ -1,31 +1,21 @@
|
|
1 |
-
|
2 |
-
from fastapi.responses import FileResponse, HTMLResponse
|
3 |
-
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
4 |
from typing import Optional
|
5 |
-
|
6 |
from utils.process_video import process_video
|
7 |
from utils.zip_response import zip_response
|
8 |
from utils.api_configs import api_configs
|
9 |
from utils.read_html import read_html
|
10 |
-
from utils.archiver import archiver
|
11 |
from utils.logger import setup_logger
|
12 |
-
|
|
|
|
|
|
|
|
|
13 |
|
14 |
app = FastAPI()
|
15 |
security = HTTPBasic()
|
16 |
api_configs_file = os.path.abspath("api_config.yml")
|
17 |
|
18 |
-
async def get_current_user(credentials: HTTPBasicCredentials = Depends(security)):
|
19 |
-
correct_username = secrets.compare_digest(credentials.username, api_configs(api_configs_file)["secrets"]["username"])
|
20 |
-
correct_password = secrets.compare_digest(credentials.password, api_configs(api_configs_file)["secrets"]["password"])
|
21 |
-
if not (correct_username and correct_password):
|
22 |
-
raise HTTPException(
|
23 |
-
status_code=401,
|
24 |
-
detail="Incorrect email or password",
|
25 |
-
headers={"WWW-Authenticate": "Basic"},
|
26 |
-
)
|
27 |
-
return credentials.username
|
28 |
-
|
29 |
class MP4Video(BaseModel):
|
30 |
video_file: UploadFile
|
31 |
|
@@ -61,7 +51,6 @@ class SRTFile(BaseModel):
|
|
61 |
raise HTTPException(status_code=422, detail='Invalid subtitle file type. Please upload an SRT file.')
|
62 |
return v
|
63 |
|
64 |
-
|
65 |
@app.get("/")
|
66 |
async def root():
|
67 |
html_content = f"""
|
@@ -77,6 +66,13 @@ async def get_form():
|
|
77 |
"""
|
78 |
return HTMLResponse(content=html_content)
|
79 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
@app.post("/process_video/")
|
81 |
async def process_video_api(video_file: MP4Video = Depends(),
|
82 |
srt_file: SRTFile = Depends(),
|
@@ -87,40 +83,36 @@ async def process_video_api(video_file: MP4Video = Depends(),
|
|
87 |
bg_color: Optional[str] = Form("#070a13b3"),
|
88 |
text_color: Optional[str] = Form("white"),
|
89 |
caption_mode: Optional[str] = Form("desktop"),
|
90 |
-
|
91 |
):
|
92 |
try:
|
93 |
logging.info("Creating temporary directories")
|
94 |
-
|
95 |
-
|
96 |
-
temp_vid_dir = os.path.join(temp_dir,video_file.filename.split('.')[0])
|
97 |
-
os.makedirs(temp_vid_dir, exist_ok=True)
|
98 |
-
temp_input_path = os.path.join(temp_vid_dir, video_file.filename)
|
99 |
-
logging.info("Copying video UploadFile to the temp_input_path")
|
100 |
-
with open(temp_input_path, 'wb') as buffer:
|
101 |
try:
|
102 |
-
shutil.copyfileobj(video_file.file,
|
103 |
finally:
|
104 |
video_file.file.close()
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
|
|
124 |
except Exception as e:
|
125 |
raise HTTPException(status_code=500, detail=str(e))
|
126 |
|
|
|
1 |
+
import shutil, os, logging, uvicorn, tempfile
|
|
|
|
|
2 |
from typing import Optional
|
3 |
+
|
4 |
from utils.process_video import process_video
|
5 |
from utils.zip_response import zip_response
|
6 |
from utils.api_configs import api_configs
|
7 |
from utils.read_html import read_html
|
|
|
8 |
from utils.logger import setup_logger
|
9 |
+
|
10 |
+
from fastapi import FastAPI, UploadFile, HTTPException, Form, Depends
|
11 |
+
from fastapi.responses import HTMLResponse, Response
|
12 |
+
from fastapi.security import HTTPBasic
|
13 |
+
from pydantic import BaseModel, field_validator
|
14 |
|
15 |
app = FastAPI()
|
16 |
security = HTTPBasic()
|
17 |
api_configs_file = os.path.abspath("api_config.yml")
|
18 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
class MP4Video(BaseModel):
|
20 |
video_file: UploadFile
|
21 |
|
|
|
51 |
raise HTTPException(status_code=422, detail='Invalid subtitle file type. Please upload an SRT file.')
|
52 |
return v
|
53 |
|
|
|
54 |
@app.get("/")
|
55 |
async def root():
|
56 |
html_content = f"""
|
|
|
66 |
"""
|
67 |
return HTMLResponse(content=html_content)
|
68 |
|
69 |
+
async def get_temp_dir():
|
70 |
+
dir = tempfile.TemporaryDirectory()
|
71 |
+
try:
|
72 |
+
yield dir.name
|
73 |
+
finally:
|
74 |
+
del dir
|
75 |
+
|
76 |
@app.post("/process_video/")
|
77 |
async def process_video_api(video_file: MP4Video = Depends(),
|
78 |
srt_file: SRTFile = Depends(),
|
|
|
83 |
bg_color: Optional[str] = Form("#070a13b3"),
|
84 |
text_color: Optional[str] = Form("white"),
|
85 |
caption_mode: Optional[str] = Form("desktop"),
|
86 |
+
temp_dir: str = Depends(get_temp_dir)
|
87 |
):
|
88 |
try:
|
89 |
logging.info("Creating temporary directories")
|
90 |
+
with open(os.path.join(temp_dir, video_file.filename), 'w+b') as temp_file:
|
91 |
+
logging.info("Copying video UploadFile to the temporary directory")
|
|
|
|
|
|
|
|
|
|
|
92 |
try:
|
93 |
+
shutil.copyfileobj(video_file.file, temp_file)
|
94 |
finally:
|
95 |
video_file.file.close()
|
96 |
+
logging.info("Copying SRT UploadFile to the temp_input_path")
|
97 |
+
if srt_file.size > 0:
|
98 |
+
with open(os.path.join(temp_dir, f"{video_file.filename.split('.')[0]}.srt"), 'w+b') as temp_srt_file:
|
99 |
+
try:
|
100 |
+
shutil.copyfileobj(srt_file.file, temp_srt_file)
|
101 |
+
finally:
|
102 |
+
srt_file.file.close()
|
103 |
+
logging.info("Processing the video...")
|
104 |
+
output_path, _ = process_video(temp_file.name, temp_srt_file.name, task, max_words_per_line, fontsize, font, bg_color, text_color, caption_mode)
|
105 |
+
logging.info("Zipping response...")
|
106 |
+
with open(os.path.join(temp_dir, f"{video_file.filename.split('.')[0]}.zip"), 'w+b') as temp_zip_file:
|
107 |
+
zip_file = zip_response(temp_zip_file.name, [output_path, srt_path])
|
108 |
+
return Response(content = zip_file)
|
109 |
+
with open(os.path.join(temp_dir, f"{video_file.filename.split('.')[0]}.srt"), 'w+b') as temp_srt_file:
|
110 |
+
logging.info("Processing the video...")
|
111 |
+
output_path, srt_path = process_video(temp_file.name, None, task, max_words_per_line, fontsize, font, bg_color, text_color, caption_mode, api_configs_file)
|
112 |
+
logging.info("Zipping response...")
|
113 |
+
with open(os.path.join(temp_dir, f"{video_file.filename.split('.')[0]}.zip"), 'w+b') as temp_zip_file:
|
114 |
+
zip_file = zip_response(temp_zip_file.name, [output_path, srt_path])
|
115 |
+
return Response(content = zip_file)
|
116 |
except Exception as e:
|
117 |
raise HTTPException(status_code=500, detail=str(e))
|
118 |
|
utils/archiver.py
DELETED
@@ -1,17 +0,0 @@
|
|
1 |
-
import shutil, os
|
2 |
-
from datetime import datetime
|
3 |
-
|
4 |
-
def archiver(timestamp:datetime=datetime.now()):
|
5 |
-
TIME = f"{timestamp.year:4d}-{timestamp.month:02d}-{timestamp.day:02d}_{timestamp.hour:02d}-{timestamp.minute:02d}"
|
6 |
-
ARCHIVE = os.path.abspath(f"archive/{TIME}")
|
7 |
-
TEMP_DIR = os.path.abspath("temp/")
|
8 |
-
LOG_FILE = os.path.abspath("main.log")
|
9 |
-
if os.path.exists(TEMP_DIR):
|
10 |
-
shutil.make_archive(os.path.join(ARCHIVE, "files"), 'zip', TEMP_DIR)
|
11 |
-
shutil.rmtree(TEMP_DIR)
|
12 |
-
if os.path.exists(LOG_FILE):
|
13 |
-
shutil.copy(LOG_FILE, os.path.join(ARCHIVE, f"{TIME}.log"))
|
14 |
-
os.remove(LOG_FILE)
|
15 |
-
|
16 |
-
if __name__ == '__main__':
|
17 |
-
archiver(timestamp=datetime.now())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/process_video.py
CHANGED
@@ -1,17 +1,9 @@
|
|
|
|
1 |
from utils.transcriber import transcriber
|
2 |
from utils.subtitler import subtitler
|
3 |
-
import logging, os
|
4 |
-
|
5 |
-
# Set up logging
|
6 |
-
logging.basicConfig(filename='main.log',
|
7 |
-
encoding='utf-8',
|
8 |
-
level=logging.DEBUG,
|
9 |
-
format='%(asctime)s %(levelname)s %(message)s',
|
10 |
-
datefmt='%m/%d/%Y %I:%M:%S %p')
|
11 |
|
12 |
-
|
13 |
-
|
14 |
-
srt_path: str,
|
15 |
task: str,
|
16 |
max_words_per_line:int,
|
17 |
fontsize:str,
|
@@ -21,17 +13,15 @@ def process_video(invideo_filename:str,
|
|
21 |
caption_mode:str,
|
22 |
config_file:str
|
23 |
):
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
subtitler(
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
subtitler(invideo_filename, SRT_PATH, OUTVIDEO_PATH, fontsize, font, bg_color, text_color, caption_mode)
|
37 |
-
return OUTVIDEO_PATH, SRT_PATH
|
|
|
1 |
+
import logging, os
|
2 |
from utils.transcriber import transcriber
|
3 |
from utils.subtitler import subtitler
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
|
5 |
+
def process_video(invideo_file: str,
|
6 |
+
srt_file: str | None,
|
|
|
7 |
task: str,
|
8 |
max_words_per_line:int,
|
9 |
fontsize:str,
|
|
|
13 |
caption_mode:str,
|
14 |
config_file:str
|
15 |
):
|
16 |
+
invideo_path_parts = os.path.normpath(invideo_file).split(os.path.sep)
|
17 |
+
VIDEO_NAME = os.path.basename(invideo_file)
|
18 |
+
OUTVIDEO_PATH = os.path.join(os.path.normpath('/'.join(invideo_path_parts[:-1])), f"result_{VIDEO_NAME}")
|
19 |
+
if srt_file:
|
20 |
+
logging.info("Subtitling...")
|
21 |
+
subtitler(invideo_file, srt_file, OUTVIDEO_PATH, fontsize, font, bg_color, text_color, caption_mode)
|
22 |
+
else:
|
23 |
+
srt_file = os.path.normpath(f"{invideo_file.split('.')[0]}.srt")
|
24 |
+
transcriber(invideo_file, srt_file, max_words_per_line, task, config_file)
|
25 |
+
logging.info("Subtitling...")
|
26 |
+
subtitler(invideo_file, srt_file, OUTVIDEO_PATH, fontsize, font, bg_color, text_color, caption_mode)
|
27 |
+
return OUTVIDEO_PATH, srt_file
|
|
|
|
utils/transcriber.py
CHANGED
@@ -1,19 +1,19 @@
|
|
1 |
from gradio_client import Client, handle_file
|
2 |
from utils.api_configs import api_configs
|
|
|
3 |
|
4 |
-
def transcriber(
|
5 |
max_words_per_line:int, task:str,
|
6 |
config_file:str):
|
7 |
-
|
8 |
HF_TOKEN = api_configs(config_file)["secrets"]["hf-token"]
|
9 |
HF_SPACE = api_configs(config_file)["secrets"]["hf-space"]
|
10 |
client = Client(HF_SPACE, hf_token=HF_TOKEN)
|
11 |
result = client.predict(
|
12 |
-
video_input=handle_file(
|
13 |
max_words_per_line=max_words_per_line,
|
14 |
task=task,
|
15 |
api_name="/predict"
|
16 |
)
|
17 |
-
with open(
|
18 |
file.write(result[0])
|
19 |
-
return
|
|
|
1 |
from gradio_client import Client, handle_file
|
2 |
from utils.api_configs import api_configs
|
3 |
+
import tempfile
|
4 |
|
5 |
+
def transcriber(invideo_file:str, srt_file:str,
|
6 |
max_words_per_line:int, task:str,
|
7 |
config_file:str):
|
|
|
8 |
HF_TOKEN = api_configs(config_file)["secrets"]["hf-token"]
|
9 |
HF_SPACE = api_configs(config_file)["secrets"]["hf-space"]
|
10 |
client = Client(HF_SPACE, hf_token=HF_TOKEN)
|
11 |
result = client.predict(
|
12 |
+
video_input=handle_file(invideo_file),
|
13 |
max_words_per_line=max_words_per_line,
|
14 |
task=task,
|
15 |
api_name="/predict"
|
16 |
)
|
17 |
+
with open(srt_file, "w", encoding='utf-8') as file:
|
18 |
file.write(result[0])
|
19 |
+
return srt_file
|
utils/zip_response.py
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
import zipfile, os
|
2 |
|
3 |
-
def zip_response(
|
4 |
-
|
5 |
-
with zipfile.ZipFile(archive_name, 'w') as zipf:
|
6 |
-
# Add specific files to the zip file
|
7 |
for file in files:
|
8 |
zipf.write(file, arcname=os.path.basename(file))
|
9 |
-
|
|
|
|
|
|
1 |
import zipfile, os
|
2 |
|
3 |
+
def zip_response(temp_zip_file: str, files: list):
|
4 |
+
with zipfile.ZipFile(temp_zip_file, 'w') as zipf:
|
|
|
|
|
5 |
for file in files:
|
6 |
zipf.write(file, arcname=os.path.basename(file))
|
7 |
+
with open(temp_zip_file, 'rb') as zip_file:
|
8 |
+
zip_bytes = zip_file.read()
|
9 |
+
return zip_bytes
|