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

improve form layout, styling and responsiveness. added a caption_width param for small screens. fixed resolution issue.

Browse files
main.py CHANGED
@@ -92,6 +92,7 @@ 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
  username: str = Depends(get_current_user)
96
  ):
97
  try:
@@ -116,12 +117,12 @@ async def process_video_api(video_file: MP4Video = Depends(),
116
  finally:
117
  srt_file.file.close()
118
  logging.info("Processing the video...")
119
- output_path, _ = process_video(temp_input_path, SRT_PATH, task, max_words_per_line, fontsize, font, bg_color, text_color)
120
  logging.info("Zipping response...")
121
  zip_path = zip_response(os.path.join(temp_vid_dir,"archive.zip"), [output_path, SRT_PATH])
122
  return FileResponse(zip_path, media_type='application/zip', filename=f"result_{video_file.filename.split('.')[0]}.zip")
123
  logging.info("Processing the video...")
124
- output_path, srt_path = process_video(temp_input_path, None, task, max_words_per_line, fontsize, font, bg_color, text_color)
125
  logging.info("Zipping response...")
126
  zip_path = zip_response(os.path.join(temp_vid_dir,"archive.zip"), [output_path, srt_path])
127
  return FileResponse(zip_path, media_type='application/zip', filename=f"result_{video_file.filename.split('.')[0]}.zip")
 
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:
 
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")
static/submit_video.html CHANGED
@@ -9,49 +9,70 @@
9
  background-color: #f0f0f0;
10
  color: #333;
11
  line-height: 1.6;
 
 
 
 
 
12
  }
13
 
14
  form {
15
- max-width: 400px;
16
- margin: 10px auto;
17
- padding: 4px 20px;
18
  background: #ffffff;
19
  border-radius: 8px;
20
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
21
- padding-bottom: 0.5rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  }
23
 
24
  input[type=file],
25
  input[type=number],
26
- input[type=text] {
27
- width: 95%;
28
- padding: 10px;
29
- margin-bottom: 10px;
30
- border-radius: 4px;
31
- border: 1px solid #ddd;
32
- box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);
33
- }
34
  select {
35
- width: 30%;
36
  padding: 10px;
37
  margin-bottom: 10px;
38
  border-radius: 4px;
39
  border: 1px solid #ddd;
40
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);
 
 
41
  }
42
 
43
  input[type=submit] {
44
- width: 25%;
45
  background-color: #4CAF50;
46
  color: white;
47
- padding: 7.5px 15px;
48
  border: none;
49
  border-radius: 5px;
50
  cursor: pointer;
51
- font-size: 12px;
52
- display: block;
53
- margin-right: auto;
54
- margin-left: auto;
55
  }
56
 
57
  input[type=submit]:hover {
@@ -61,13 +82,13 @@
61
  label {
62
  margin-top: 10px;
63
  display: block;
 
 
64
  }
65
 
66
  .footer {
67
  width: 100%;
68
  background-color: #f0f0f0;
69
- padding: 0;
70
- position: absolute;
71
  text-align: center;
72
  }
73
 
@@ -77,40 +98,93 @@
77
  }
78
 
79
  .fa-github:hover {
80
- transform: scale(1.2)
81
  }
82
- .fa-github{
83
- color: #000000
 
84
  }
 
85
  .fa-linkedin:hover {
86
- transform: scale(1.2)
87
  }
 
88
  .fa-linkedin {
89
- color: #0077B5
90
  }
 
91
  /* Additional Responsiveness */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  @media (max-width: 768px) {
93
  form {
94
- width: 90%;
 
 
 
 
 
 
 
 
 
 
 
 
95
  }
96
  }
97
  </style>
98
  </head>
99
  <body>
100
  <form action="/process_video/" enctype="multipart/form-data" method="post">
101
- Video File: <input type="file" name="video_file"><br>
102
- Subtitles File: <input type="file" name="srt_file"><br>
103
- <label for="task">Task</label>
104
- <select id="task" name="task">
105
- <option value="transcribe">Transcribe</option>
106
- <option value="translate">Translate</option>
107
- </select><br>
108
- Max words per line: <input type="number" name="max_words_per_line" value="6"><br>
109
- Font size: <input type="number" name="fontsize" value="42"><br>
110
- Font: <input type="text" name="font" value="FuturaPTHeavy"><br>
111
- Background color (Pro tip: #00FFFF00 = transparent): <input type="text" name="bg_color" value="#070a13b3"><br>
112
- Text color: <input type="text" name="text_color" value="white"><br>
113
- <input type="submit">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  </form>
115
  <!-- Footer -->
116
  <div class="footer">
 
9
  background-color: #f0f0f0;
10
  color: #333;
11
  line-height: 1.6;
12
+ margin: 0;
13
+ padding: 0;
14
+ display: flex;
15
+ flex-direction: column;
16
+ min-height: 100vh;
17
  }
18
 
19
  form {
20
+ max-width: 900px;
21
+ margin: .9rem auto;
22
+ padding: 1rem;
23
  background: #ffffff;
24
  border-radius: 8px;
25
+ box-shadow: 0 0 15px rgba(0, 0, 0, 0.1);
26
+ display: flex;
27
+ flex-direction: column;
28
+ }
29
+
30
+ .form-wrapper {
31
+ display: flex;
32
+ flex-wrap: wrap;
33
+ gap: 20px;
34
+ }
35
+
36
+ .form-group {
37
+ flex: 1;
38
+ min-width: calc(50% - 20px);
39
+ box-sizing: border-box;
40
+ }
41
+
42
+ .form-group h3 {
43
+ margin-bottom: 15px;
44
+ color: #4CAF50;
45
+ font-size: 18px;
46
+ border-bottom: 2px solid #4CAF50;
47
+ padding-bottom: 5px;
48
  }
49
 
50
  input[type=file],
51
  input[type=number],
52
+ input[type=text],
 
 
 
 
 
 
 
53
  select {
54
+ width: 100%;
55
  padding: 10px;
56
  margin-bottom: 10px;
57
  border-radius: 4px;
58
  border: 1px solid #ddd;
59
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);
60
+ font-size: 13px;
61
+ box-sizing: border-box; /* Ensure padding and border are included in the element's total width and height */
62
  }
63
 
64
  input[type=submit] {
65
+ width: 100%;
66
  background-color: #4CAF50;
67
  color: white;
68
+ padding: 12px 18px;
69
  border: none;
70
  border-radius: 5px;
71
  cursor: pointer;
72
+ font-size: 15px;
73
+ margin-top: 20px;
74
+ transition: background-color 0.3s ease;
75
+ box-sizing: border-box;
76
  }
77
 
78
  input[type=submit]:hover {
 
82
  label {
83
  margin-top: 10px;
84
  display: block;
85
+ font-weight: bold;
86
+ font-size: 13px;
87
  }
88
 
89
  .footer {
90
  width: 100%;
91
  background-color: #f0f0f0;
 
 
92
  text-align: center;
93
  }
94
 
 
98
  }
99
 
100
  .fa-github:hover {
101
+ transform: scale(1.2);
102
  }
103
+
104
+ .fa-github {
105
+ color: #000000;
106
  }
107
+
108
  .fa-linkedin:hover {
109
+ transform: scale(1.2);
110
  }
111
+
112
  .fa-linkedin {
113
+ color: #0077B5;
114
  }
115
+
116
  /* Additional Responsiveness */
117
+ @media (max-width: 992px) {
118
+ form {
119
+ max-width: 90%;
120
+ margin-left: 15%;
121
+ margin-right: 15%;
122
+ padding: 15px;
123
+ }
124
+
125
+ .form-wrapper {
126
+ flex-direction: column;
127
+ }
128
+
129
+ .form-group {
130
+ min-width: 100%;
131
+ }
132
+ }
133
+
134
  @media (max-width: 768px) {
135
  form {
136
+ max-width: 90%;
137
+ margin-left: 10%;
138
+ margin-right: 10%;
139
+ padding: 15px;
140
+ }
141
+ }
142
+
143
+ @media (max-width: 480px) {
144
+ form {
145
+ max-width: 90%;
146
+ margin-left: 5%;
147
+ margin-right: 5%;
148
+ padding: 10px;
149
  }
150
  }
151
  </style>
152
  </head>
153
  <body>
154
  <form action="/process_video/" enctype="multipart/form-data" method="post">
155
+ <div class="form-wrapper">
156
+ <div class="form-group">
157
+ <h3>Inputs & Task Selection</h3>
158
+ <label for="video_file">Video File</label>
159
+ <input type="file" id="video_file" name="video_file"><br>
160
+ <label for="srt_file">Subtitles File</label>
161
+ <input type="file" id="srt_file" name="srt_file"><br>
162
+ <label for="task">Task</label>
163
+ <select id="task" name="task">
164
+ <option value="transcribe">Transcribe</option>
165
+ <option value="translate">Translate</option>
166
+ </select>
167
+ </div>
168
+ <div class="form-group">
169
+ <h3>Visual Parameters</h3>
170
+ <label for="max_words_per_line">Max words per line</label>
171
+ <input type="number" id="max_words_per_line" name="max_words_per_line" value="6"><br>
172
+ <label for="fontsize">Font size</label>
173
+ <input type="number" id="fontsize" name="fontsize" value="42"><br>
174
+ <label for="font">Font:</label>
175
+ <input type="text" id="font" name="font" value="FuturaPTHeavy"><br>
176
+ <label for="bg_color">Background color</label>
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>
185
+ </div>
186
+ </div>
187
+ <input type="submit" value="Submit">
188
  </form>
189
  <!-- Footer -->
190
  <div class="footer">
utils/process_video.py CHANGED
@@ -18,14 +18,15 @@ def process_video(invideo_filename:str,
18
  fontsize:str,
19
  font:str,
20
  bg_color:str,
21
- text_color:str
 
22
  ):
23
  invideo_filename = os.path.normpath(invideo_filename)
24
  invideo_path_parts = invideo_filename.split(os.path.sep)
25
  VIDEO_NAME = invideo_path_parts[-1]
26
  OUTVIDEO_PATH = os.path.join(invideo_path_parts[-3], invideo_path_parts[-2], f"result_{VIDEO_NAME}")
27
  if srt_path:
28
- subtitler(invideo_filename, srt_path, OUTVIDEO_PATH, fontsize, font, bg_color, text_color)
29
  return OUTVIDEO_PATH, srt_path
30
  logging.info("Converting Video to Audio")
31
  INAUDIO_PATH = os.path.abspath(f"{invideo_filename.split('.')[0]}.m4a")
@@ -36,5 +37,5 @@ def process_video(invideo_filename:str,
36
  if not os.path.exists(SRT_PATH):
37
  transcriber(INAUDIO_PATH, SRT_PATH, max_words_per_line, task)
38
  logging.info("Subtitling...")
39
- subtitler(invideo_filename, SRT_PATH, OUTVIDEO_PATH, fontsize, font, bg_color, text_color)
40
  return OUTVIDEO_PATH, SRT_PATH
 
18
  fontsize: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
  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
utils/subtitler.py CHANGED
@@ -19,6 +19,12 @@ def parse_srt(srt_file):
19
  i += 1
20
  return subtitles
21
 
 
 
 
 
 
 
22
 
23
  def subtitler(video_file:str,
24
  srt_path:str,
@@ -26,23 +32,25 @@ def subtitler(video_file:str,
26
  fontsize:int,
27
  font: str,
28
  bg_color:str,
29
- text_color:str
 
30
  ):
31
  """Add subtitles from an SRT file to a video."""
32
  video_file = os.path.abspath(video_file)
33
  srt_path = os.path.abspath(srt_path)
34
  output_file = os.path.abspath(output_file)
35
- clip = VideoFileClip(filename=video_file, target_resolution=None, resize_algorithm='bitexact')
36
  subtitles = parse_srt(srt_path)
37
  subtitle_clips = []
38
  for start, end, text in subtitles:
39
  # Create TextClip with specified styling
 
40
  txt_clip = TextClip(text, fontsize=fontsize, color=text_color, font=font, method='caption',
41
- bg_color=bg_color, align='center', size=(clip.w*1/2, None))
42
  txt_clip = txt_clip.set_position(('center', 'bottom')).set_duration(clip.duration).set_start(start).set_end(end)
43
  subtitle_x_position = 'center'
44
  subtitle_y_position = clip.h * 4 / 5
45
  text_position = (subtitle_x_position, subtitle_y_position)
46
  subtitle_clips.append(txt_clip.set_position(text_position))
47
- video = CompositeVideoClip(size=(clip.h,clip.w), clips=[clip] + subtitle_clips)
48
  video.write_videofile(output_file, codec='libx264', audio_codec='aac')
 
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,
 
32
  fontsize:int,
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)
40
  srt_path = os.path.abspath(srt_path)
41
  output_file = os.path.abspath(output_file)
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)
56
  video.write_videofile(output_file, codec='libx264', audio_codec='aac')
utils/transcriber.py CHANGED
@@ -14,12 +14,9 @@ def convert_seconds_to_time(seconds):
14
  hours, remainder = divmod(seconds, 3600)
15
  minutes, remainder = divmod(remainder, 60)
16
  whole_seconds = int(remainder)
17
- milliseconds = int((remainder - whole_seconds) * 1000)
 
18
 
19
- # Format the time string
20
- time_string = f"{int(hours):02}:{int(minutes):02}:{whole_seconds:02},{milliseconds:03}"
21
-
22
- return time_string
23
 
24
  def write_srt(segments, srt_path, max_words_per_line):
25
  """Write segments to an SRT file with a maximum number of words per line."""
 
14
  hours, remainder = divmod(seconds, 3600)
15
  minutes, remainder = divmod(remainder, 60)
16
  whole_seconds = int(remainder)
17
+ milliseconds = int((remainder - whole_seconds) * 1000)
18
+ return f"{int(hours):02}:{int(minutes):02}:{whole_seconds:02},{milliseconds:03}"
19
 
 
 
 
 
20
 
21
  def write_srt(segments, srt_path, max_words_per_line):
22
  """Write segments to an SRT file with a maximum number of words per line."""