marquesafonso commited on
Commit
d0c0836
·
1 Parent(s): 2dcfc88

add height ratio for better caption mode. pydantic v2 field validation. add archiver to main.

Browse files
docker-compose.yml CHANGED
@@ -1,5 +1,3 @@
1
- version: '3.8'
2
-
3
  services:
4
  app:
5
  build:
 
 
 
1
  services:
2
  app:
3
  build:
main.py CHANGED
@@ -2,11 +2,12 @@ from fastapi import FastAPI, UploadFile, HTTPException, Form, Depends
2
  from fastapi.responses import FileResponse, HTMLResponse
3
  from fastapi.security import HTTPBasic, HTTPBasicCredentials
4
  from typing import Optional
5
- from pydantic import BaseModel, validator
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
  import shutil, os, logging, uvicorn, secrets
11
 
12
  #TODO: upgrade project dependencies for the soon to be released version of faster-whisper that supports distil-largev3
@@ -42,7 +43,7 @@ class MP4Video(BaseModel):
42
  def file(self):
43
  return self.video_file.file
44
 
45
- @validator('video_file')
46
  def validate_video_file(cls, v):
47
  if not v.filename.endswith('.mp4'):
48
  raise HTTPException(status_code=500, detail='Invalid video file type. Please upload an MP4 file.')
@@ -61,7 +62,7 @@ class SRTFile(BaseModel):
61
  def size(self):
62
  return self.srt_file.size
63
 
64
- @validator('srt_file')
65
  def validate_srt_file(cls, v):
66
  if v.size > 0 and not v.filename.endswith('.srt'):
67
  raise HTTPException(status_code=422, detail='Invalid subtitle file type. Please upload an SRT file.')
@@ -92,11 +93,12 @@ async def process_video_api(video_file: MP4Video = Depends(),
92
  font: Optional[str] = Form("FuturaPTHeavy"),
93
  bg_color: Optional[str] = Form("#070a13b3"),
94
  text_color: Optional[str] = Form("white"),
95
- caption_width: Optional[str] = Form("desktop"),
96
  username: str = Depends(get_current_user)
97
  ):
98
  try:
99
  logging.info("Creating temporary directories")
 
100
  temp_dir = os.path.join(os.getcwd(),"temp")
101
  os.makedirs(temp_dir, exist_ok=True)
102
  temp_vid_dir = os.path.join(temp_dir,video_file.filename.split('.')[0])
@@ -117,12 +119,12 @@ async def process_video_api(video_file: MP4Video = Depends(),
117
  finally:
118
  srt_file.file.close()
119
  logging.info("Processing the video...")
120
- output_path, _ = process_video(temp_input_path, SRT_PATH, task, max_words_per_line, fontsize, font, bg_color, text_color, caption_width)
121
  logging.info("Zipping response...")
122
  zip_path = zip_response(os.path.join(temp_vid_dir,"archive.zip"), [output_path, SRT_PATH])
123
  return FileResponse(zip_path, media_type='application/zip', filename=f"result_{video_file.filename.split('.')[0]}.zip")
124
  logging.info("Processing the video...")
125
- output_path, srt_path = process_video(temp_input_path, None, task, max_words_per_line, fontsize, font, bg_color, text_color, caption_width)
126
  logging.info("Zipping response...")
127
  zip_path = zip_response(os.path.join(temp_vid_dir,"archive.zip"), [output_path, srt_path])
128
  return FileResponse(zip_path, media_type='application/zip', filename=f"result_{video_file.filename.split('.')[0]}.zip")
@@ -132,4 +134,8 @@ async def process_video_api(video_file: MP4Video = Depends(),
132
 
133
  if __name__ == "__main__":
134
  # Use Uvicorn to run the application
 
 
 
 
135
  uvicorn.run(app, host="0.0.0.0", port=8000)
 
2
  from fastapi.responses import FileResponse, HTMLResponse
3
  from fastapi.security import HTTPBasic, HTTPBasicCredentials
4
  from typing import Optional
5
+ from pydantic import BaseModel, field_validator
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
  import shutil, os, logging, uvicorn, secrets
12
 
13
  #TODO: upgrade project dependencies for the soon to be released version of faster-whisper that supports distil-largev3
 
43
  def file(self):
44
  return self.video_file.file
45
 
46
+ @field_validator('video_file')
47
  def validate_video_file(cls, v):
48
  if not v.filename.endswith('.mp4'):
49
  raise HTTPException(status_code=500, detail='Invalid video file type. Please upload an MP4 file.')
 
62
  def size(self):
63
  return self.srt_file.size
64
 
65
+ @field_validator('srt_file')
66
  def validate_srt_file(cls, v):
67
  if v.size > 0 and not v.filename.endswith('.srt'):
68
  raise HTTPException(status_code=422, detail='Invalid subtitle file type. Please upload an SRT file.')
 
93
  font: Optional[str] = Form("FuturaPTHeavy"),
94
  bg_color: Optional[str] = Form("#070a13b3"),
95
  text_color: Optional[str] = Form("white"),
96
+ caption_mode: Optional[str] = Form("desktop"),
97
  username: str = Depends(get_current_user)
98
  ):
99
  try:
100
  logging.info("Creating temporary directories")
101
+ print(caption_mode)
102
  temp_dir = os.path.join(os.getcwd(),"temp")
103
  os.makedirs(temp_dir, exist_ok=True)
104
  temp_vid_dir = os.path.join(temp_dir,video_file.filename.split('.')[0])
 
119
  finally:
120
  srt_file.file.close()
121
  logging.info("Processing the video...")
122
+ output_path, _ = process_video(temp_input_path, SRT_PATH, task, max_words_per_line, fontsize, font, bg_color, text_color, caption_mode)
123
  logging.info("Zipping response...")
124
  zip_path = zip_response(os.path.join(temp_vid_dir,"archive.zip"), [output_path, SRT_PATH])
125
  return FileResponse(zip_path, media_type='application/zip', filename=f"result_{video_file.filename.split('.')[0]}.zip")
126
  logging.info("Processing the video...")
127
+ output_path, srt_path = process_video(temp_input_path, None, task, max_words_per_line, fontsize, font, bg_color, text_color, caption_mode)
128
  logging.info("Zipping response...")
129
  zip_path = zip_response(os.path.join(temp_vid_dir,"archive.zip"), [output_path, srt_path])
130
  return FileResponse(zip_path, media_type='application/zip', filename=f"result_{video_file.filename.split('.')[0]}.zip")
 
134
 
135
  if __name__ == "__main__":
136
  # Use Uvicorn to run the application
137
+ try:
138
+ archiver()
139
+ except FileNotFoundError:
140
+ pass
141
  uvicorn.run(app, host="0.0.0.0", port=8000)
static/submit_video.html CHANGED
@@ -177,8 +177,8 @@
177
  <input type="text" id="bg_color" name="bg_color" value="#00FFFF00"><br>
178
  <label for="text_color">Text color</label>
179
  <input type="text" id="text_color" name="text_color" value="white"><br>
180
- <label for="caption_width">Caption width</label>
181
- <select id="caption_width" name="caption_width">
182
  <option value="desktop">Desktop</option>
183
  <option value="mobile">Mobile</option>
184
  </select>
 
177
  <input type="text" id="bg_color" name="bg_color" value="#00FFFF00"><br>
178
  <label for="text_color">Text color</label>
179
  <input type="text" id="text_color" name="text_color" value="white"><br>
180
+ <label for="caption_mode">Caption mode</label>
181
+ <select id="caption_mode" name="caption_mode">
182
  <option value="desktop">Desktop</option>
183
  <option value="mobile">Mobile</option>
184
  </select>
utils/archiver.py CHANGED
@@ -1,7 +1,7 @@
1
  import shutil, os
2
  from datetime import datetime
3
 
4
- def archiver(timestamp:datetime):
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/")
 
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/")
utils/process_video.py CHANGED
@@ -19,14 +19,14 @@ def process_video(invideo_filename:str,
19
  font:str,
20
  bg_color:str,
21
  text_color:str,
22
- caption_width:str
23
  ):
24
  invideo_filename = os.path.normpath(invideo_filename)
25
  invideo_path_parts = invideo_filename.split(os.path.sep)
26
  VIDEO_NAME = invideo_path_parts[-1]
27
  OUTVIDEO_PATH = os.path.join(invideo_path_parts[-3], invideo_path_parts[-2], f"result_{VIDEO_NAME}")
28
  if srt_path:
29
- subtitler(invideo_filename, srt_path, OUTVIDEO_PATH, fontsize, font, bg_color, text_color, caption_width)
30
  return OUTVIDEO_PATH, srt_path
31
  logging.info("Converting Video to Audio")
32
  INAUDIO_PATH = os.path.abspath(f"{invideo_filename.split('.')[0]}.m4a")
@@ -37,5 +37,5 @@ def process_video(invideo_filename:str,
37
  if not os.path.exists(SRT_PATH):
38
  transcriber(INAUDIO_PATH, SRT_PATH, max_words_per_line, task)
39
  logging.info("Subtitling...")
40
- subtitler(invideo_filename, SRT_PATH, OUTVIDEO_PATH, fontsize, font, bg_color, text_color, caption_width)
41
  return OUTVIDEO_PATH, SRT_PATH
 
19
  font:str,
20
  bg_color:str,
21
  text_color:str,
22
+ caption_mode:str
23
  ):
24
  invideo_filename = os.path.normpath(invideo_filename)
25
  invideo_path_parts = invideo_filename.split(os.path.sep)
26
  VIDEO_NAME = invideo_path_parts[-1]
27
  OUTVIDEO_PATH = os.path.join(invideo_path_parts[-3], invideo_path_parts[-2], f"result_{VIDEO_NAME}")
28
  if srt_path:
29
+ subtitler(invideo_filename, srt_path, OUTVIDEO_PATH, fontsize, font, bg_color, text_color, caption_mode)
30
  return OUTVIDEO_PATH, srt_path
31
  logging.info("Converting Video to Audio")
32
  INAUDIO_PATH = os.path.abspath(f"{invideo_filename.split('.')[0]}.m4a")
 
37
  if not os.path.exists(SRT_PATH):
38
  transcriber(INAUDIO_PATH, SRT_PATH, max_words_per_line, task)
39
  logging.info("Subtitling...")
40
+ subtitler(invideo_filename, SRT_PATH, OUTVIDEO_PATH, fontsize, font, bg_color, text_color, caption_mode)
41
  return OUTVIDEO_PATH, SRT_PATH
utils/subtitler.py CHANGED
@@ -19,12 +19,14 @@ def parse_srt(srt_file):
19
  i += 1
20
  return subtitles
21
 
22
- def filter_caption_width(caption_width:str='desktop'):
23
- if caption_width == 'desktop':
24
- caption_width = 0.2
25
- elif caption_width == 'mobile':
26
- caption_width = 0.5
27
- return caption_width
 
 
28
 
29
  def subtitler(video_file:str,
30
  srt_path:str,
@@ -33,7 +35,7 @@ def subtitler(video_file:str,
33
  font: str,
34
  bg_color:str,
35
  text_color:str,
36
- caption_width:str
37
  ):
38
  """Add subtitles from an SRT file to a video."""
39
  video_file = os.path.abspath(video_file)
@@ -42,14 +44,15 @@ def subtitler(video_file:str,
42
  clip = VideoFileClip(filename=video_file, target_resolution=None)
43
  subtitles = parse_srt(srt_path)
44
  subtitle_clips = []
 
45
  for start, end, text in subtitles:
46
  # Create TextClip with specified styling
47
  # To get a list of possible color and font values run: print(TextClip.list("font"), '\n\n', TextClip.list("color"))
48
  txt_clip = TextClip(text, fontsize=fontsize, color=text_color, font=font, method='caption',
49
- bg_color=bg_color, align='center', size=(clip.w*filter_caption_width(caption_width), None))
50
  txt_clip = txt_clip.set_position(('center', 'bottom')).set_duration(clip.duration).set_start(start).set_end(end)
51
  subtitle_x_position = 'center'
52
- subtitle_y_position = clip.h * 4 / 5
53
  text_position = (subtitle_x_position, subtitle_y_position)
54
  subtitle_clips.append(txt_clip.set_position(text_position))
55
  video = CompositeVideoClip(size=None, clips=[clip] + subtitle_clips)
 
19
  i += 1
20
  return subtitles
21
 
22
+ def filter_caption_width(caption_mode:str):
23
+ if caption_mode == 'desktop':
24
+ caption_width_ratio = 0.5
25
+ caption_height_ratio = 0.8
26
+ elif caption_mode == 'mobile':
27
+ caption_width_ratio = 0.2
28
+ caption_height_ratio = 0.7
29
+ return caption_width_ratio, caption_height_ratio
30
 
31
  def subtitler(video_file:str,
32
  srt_path:str,
 
35
  font: str,
36
  bg_color:str,
37
  text_color:str,
38
+ caption_mode:str
39
  ):
40
  """Add subtitles from an SRT file to a video."""
41
  video_file = os.path.abspath(video_file)
 
44
  clip = VideoFileClip(filename=video_file, target_resolution=None)
45
  subtitles = parse_srt(srt_path)
46
  subtitle_clips = []
47
+ caption_width_ratio, caption_height_ratio = filter_caption_width(caption_mode)
48
  for start, end, text in subtitles:
49
  # Create TextClip with specified styling
50
  # To get a list of possible color and font values run: print(TextClip.list("font"), '\n\n', TextClip.list("color"))
51
  txt_clip = TextClip(text, fontsize=fontsize, color=text_color, font=font, method='caption',
52
+ bg_color=bg_color, align='center', size=(clip.w*caption_width_ratio, None))
53
  txt_clip = txt_clip.set_position(('center', 'bottom')).set_duration(clip.duration).set_start(start).set_end(end)
54
  subtitle_x_position = 'center'
55
+ subtitle_y_position = clip.h * caption_height_ratio
56
  text_position = (subtitle_x_position, subtitle_y_position)
57
  subtitle_clips.append(txt_clip.set_position(text_position))
58
  video = CompositeVideoClip(size=None, clips=[clip] + subtitle_clips)