Commit
·
8cdcb92
1
Parent(s):
b6093b0
add font and text color args. fix api behaviour. improve readme.
Browse files- README.md +18 -4
- cli_app.py → cli.py +17 -11
- example_cmd.txt +0 -5
- main.py +34 -14
- utils/process_video.py +9 -4
- utils/subtitler.py +8 -5
- utils/transcriber.py +1 -3
README.md
CHANGED
@@ -22,14 +22,28 @@ Note that this assumes a proper Git installation and ssh key configuration.
|
|
22 |
|
23 |
## Quick start
|
24 |
|
25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
|
27 |
```
|
28 |
-
pipenv run python .\
|
29 |
```
|
30 |
|
31 |
-
|
|
|
|
|
|
|
|
|
32 |
|
33 |
```
|
34 |
-
pipenv run
|
35 |
```
|
|
|
|
|
|
22 |
|
23 |
## Quick start
|
24 |
|
25 |
+
### Command Line Interface
|
26 |
+
|
27 |
+
Run the following code to your example using the CLI. The example is based on a youtube video url (optional):
|
28 |
+
|
29 |
+
```
|
30 |
+
pipenv run python .\cli.py --invideo_filename '<your_file_name>' --video_url 'https://www.youtube.com/watch?v=<your_youtube_video>' --max_words_per_line 8
|
31 |
+
```
|
32 |
+
|
33 |
+
Fontsize, Font, Background Color and Text Color arguments are available:
|
34 |
|
35 |
```
|
36 |
+
pipenv run python .\cli.py --invideo_filename '<your_file>' --video_url 'https://www.youtube.com/watch?v=<your_youtube_video>' --max_words_per_line 8 --fontsize 28 --font "Arial-Bold" --bg_color None --text_color 'white'
|
37 |
```
|
38 |
|
39 |
+
### API
|
40 |
+
|
41 |
+
A FastAPI API is also made available.
|
42 |
+
|
43 |
+
To start the API run:
|
44 |
|
45 |
```
|
46 |
+
pipenv run uvicorn main:app --reload
|
47 |
```
|
48 |
+
|
49 |
+
Then check the [submit_video](http://127.0.0.1:8000/submit_video/) endpoint.
|
cli_app.py → cli.py
RENAMED
@@ -11,11 +11,13 @@ logging.basicConfig(filename='main.log',
|
|
11 |
format='%(asctime)s %(levelname)s %(message)s',
|
12 |
datefmt='%m/%d/%Y %I:%M:%S %p')
|
13 |
|
14 |
-
def main(video_url,
|
15 |
-
invideo_filename,
|
16 |
-
|
17 |
-
|
18 |
-
|
|
|
|
|
19 |
):
|
20 |
INVIDEO_DIR = os.path.join('data/',invideo_filename)
|
21 |
if not os.path.exists(INVIDEO_DIR):
|
@@ -29,28 +31,32 @@ def main(video_url,
|
|
29 |
if not os.path.exists(SRT_PATH):
|
30 |
transcriber(stream_title, SRT_PATH, max_words_per_line)
|
31 |
pbar.update(33.33)
|
32 |
-
subtitler(stream_title, SRT_PATH, OUTVIDEO_PATH,fontsize, bg_color)
|
33 |
pbar.update(33.34)
|
34 |
return
|
35 |
INVIDEO_PATH = os.path.join(INVIDEO_DIR, f"{invideo_filename}.mp4")
|
36 |
if not os.path.exists(SRT_PATH):
|
37 |
transcriber(INVIDEO_PATH, SRT_PATH, max_words_per_line)
|
38 |
pbar.update(66.66)
|
39 |
-
subtitler(INVIDEO_PATH, SRT_PATH, OUTVIDEO_PATH, fontsize,bg_color)
|
40 |
pbar.update(33.34)
|
41 |
|
42 |
if __name__ == '__main__':
|
43 |
parser = ArgumentParser()
|
44 |
-
parser.add_argument('--invideo_filename', required=True, type=str, help='
|
45 |
parser.add_argument('--video_url', required=False, default=None, type=str, help='A video file to be subtitled (Optional)')
|
|
|
46 |
parser.add_argument('--fontsize', required=False, default=32, type=int, help='Font size for captions (int)')
|
47 |
-
parser.add_argument('--
|
48 |
-
parser.add_argument(
|
|
|
49 |
args = parser.parse_args()
|
50 |
# Example usage
|
51 |
main(args.video_url,
|
52 |
args.invideo_filename,
|
|
|
53 |
args.fontsize,
|
|
|
54 |
args.bg_color,
|
55 |
-
args.
|
56 |
)
|
|
|
11 |
format='%(asctime)s %(levelname)s %(message)s',
|
12 |
datefmt='%m/%d/%Y %I:%M:%S %p')
|
13 |
|
14 |
+
def main(video_url:str,
|
15 |
+
invideo_filename:str,
|
16 |
+
max_words_per_line:int,
|
17 |
+
fontsize:int,
|
18 |
+
font:str,
|
19 |
+
bg_color:str,
|
20 |
+
text_color:str
|
21 |
):
|
22 |
INVIDEO_DIR = os.path.join('data/',invideo_filename)
|
23 |
if not os.path.exists(INVIDEO_DIR):
|
|
|
31 |
if not os.path.exists(SRT_PATH):
|
32 |
transcriber(stream_title, SRT_PATH, max_words_per_line)
|
33 |
pbar.update(33.33)
|
34 |
+
subtitler(stream_title, SRT_PATH, OUTVIDEO_PATH,fontsize, font, bg_color, text_color)
|
35 |
pbar.update(33.34)
|
36 |
return
|
37 |
INVIDEO_PATH = os.path.join(INVIDEO_DIR, f"{invideo_filename}.mp4")
|
38 |
if not os.path.exists(SRT_PATH):
|
39 |
transcriber(INVIDEO_PATH, SRT_PATH, max_words_per_line)
|
40 |
pbar.update(66.66)
|
41 |
+
subtitler(INVIDEO_PATH, SRT_PATH, OUTVIDEO_PATH, fontsize, font, bg_color, text_color)
|
42 |
pbar.update(33.34)
|
43 |
|
44 |
if __name__ == '__main__':
|
45 |
parser = ArgumentParser()
|
46 |
+
parser.add_argument('--invideo_filename', required=True, type=str, help='Filename to caption.')
|
47 |
parser.add_argument('--video_url', required=False, default=None, type=str, help='A video file to be subtitled (Optional)')
|
48 |
+
parser.add_argument("--max_words_per_line", type=int, default=None, help="the maximum number of words in a segment. (int)")
|
49 |
parser.add_argument('--fontsize', required=False, default=32, type=int, help='Font size for captions (int)')
|
50 |
+
parser.add_argument('--font', required=False, default="FuturaPTHeavy", type=str, help='Font style for captions (str)')
|
51 |
+
parser.add_argument('--bg_color', required=False, default="#070a13b3", type=str, help='Hex color value for caption background colour. (str)')
|
52 |
+
parser.add_argument('--text_color', required=False, default="white", type=str, help='color value for caption text. (str)')
|
53 |
args = parser.parse_args()
|
54 |
# Example usage
|
55 |
main(args.video_url,
|
56 |
args.invideo_filename,
|
57 |
+
args.max_words_per_line,
|
58 |
args.fontsize,
|
59 |
+
args.font,
|
60 |
args.bg_color,
|
61 |
+
args.text_color
|
62 |
)
|
example_cmd.txt
DELETED
@@ -1,5 +0,0 @@
|
|
1 |
-
[example no background]
|
2 |
-
pipenv run python .\main.py --invideo_dir './data/' --invideo_filename '<your_video>.mp4' --outvideo_path './data/<output_video>.mp4' --video_url 'https://www.youtube.com/watch?v=<your_youtube_video>' --srt_path '<your_srt_file>.srt' --fontsize 28 --bg_color None
|
3 |
-
|
4 |
-
[example with background]
|
5 |
-
pipenv run python .\main.py --invideo_dir './data/' --invideo_filename '<your_video>.mp4' --outvideo_path './data/<output_video>.mp4' --video_url 'https://www.youtube.com/watch?v=<your_youtube_video>' --srt_path '<your_srt_file>.srt'
|
|
|
|
|
|
|
|
|
|
|
|
main.py
CHANGED
@@ -16,10 +16,13 @@ async def get_form():
|
|
16 |
<html>
|
17 |
<body>
|
18 |
<form action="/process_video/" enctype="multipart/form-data" method="post">
|
19 |
-
<input type="file" name="
|
20 |
-
|
21 |
-
Background color: <input type="text" name="bg_color" value="#070a13b3"><br>
|
22 |
Max words per line: <input type="number" name="max_words_per_line" value="8"><br>
|
|
|
|
|
|
|
|
|
23 |
<input type="submit">
|
24 |
</form>
|
25 |
</body>
|
@@ -28,26 +31,43 @@ async def get_form():
|
|
28 |
return HTMLResponse(content=html_content)
|
29 |
|
30 |
@app.post("/process_video/")
|
31 |
-
async def process_video_api(
|
32 |
-
|
|
|
|
|
|
|
33 |
bg_color: Optional[str] = Form("#070a13b3"),
|
34 |
-
|
|
|
35 |
try:
|
36 |
-
if not str(
|
37 |
raise HTTPException(status_code=400, detail="Invalid file type. Please upload an MP4 file.")
|
38 |
-
#
|
39 |
temp_dir = os.path.join(os.getcwd(),"temp")
|
40 |
os.makedirs(temp_dir, exist_ok=True)
|
41 |
-
temp_input_path = os.path.join(temp_dir,
|
42 |
-
# Copy UploadFile to the temp_input_path
|
43 |
with open(temp_input_path, 'wb') as buffer:
|
44 |
try:
|
45 |
-
shutil.copyfileobj(
|
46 |
finally:
|
47 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
# Process the video
|
49 |
-
output_path = process_video(temp_input_path, fontsize, bg_color,
|
|
|
50 |
# Return the processed video file
|
51 |
-
return FileResponse(output_path, media_type="video/mp4", filename=f"result_{
|
|
|
52 |
except Exception as e:
|
53 |
raise HTTPException(status_code=500, detail=str(e))
|
|
|
16 |
<html>
|
17 |
<body>
|
18 |
<form action="/process_video/" enctype="multipart/form-data" method="post">
|
19 |
+
Video File: <input type="file" name="video_file"><br>
|
20 |
+
Subtitles File: <input type="file" name="srt_file"><br>
|
|
|
21 |
Max words per line: <input type="number" name="max_words_per_line" value="8"><br>
|
22 |
+
Font size: <input type="number" name="fontsize" value="36"><br>
|
23 |
+
Font: <input type="text" name="font" value="FuturaPTHeavy"><br>
|
24 |
+
Background color: <input type="text" name="bg_color" value="#070a13b3"><br>
|
25 |
+
Text color: <input type="text" name="text_color" value="white"><br>
|
26 |
<input type="submit">
|
27 |
</form>
|
28 |
</body>
|
|
|
31 |
return HTMLResponse(content=html_content)
|
32 |
|
33 |
@app.post("/process_video/")
|
34 |
+
async def process_video_api(video_file: UploadFile = File(...),
|
35 |
+
srt_file: Optional[UploadFile] = File(...),
|
36 |
+
max_words_per_line: Optional[int] = Form(8),
|
37 |
+
fontsize: Optional[int] = Form(36),
|
38 |
+
font: Optional[str] = Form("FuturaPTHeavy"),
|
39 |
bg_color: Optional[str] = Form("#070a13b3"),
|
40 |
+
text_color: Optional[str] = Form("white")
|
41 |
+
):
|
42 |
try:
|
43 |
+
if not str(video_file.filename).endswith('.mp4'):
|
44 |
raise HTTPException(status_code=400, detail="Invalid file type. Please upload an MP4 file.")
|
45 |
+
# Create temp dir
|
46 |
temp_dir = os.path.join(os.getcwd(),"temp")
|
47 |
os.makedirs(temp_dir, exist_ok=True)
|
48 |
+
temp_input_path = os.path.join(temp_dir, video_file.filename)
|
49 |
+
# Copy video UploadFile to the temp_input_path
|
50 |
with open(temp_input_path, 'wb') as buffer:
|
51 |
try:
|
52 |
+
shutil.copyfileobj(video_file.file, buffer)
|
53 |
finally:
|
54 |
+
video_file.file.close()
|
55 |
+
# Copy srt UploadFile to the temp_input_path
|
56 |
+
if srt_file.size > 0:
|
57 |
+
SRT_PATH = os.path.abspath(f"{temp_input_path.split('.')[0]}.srt")
|
58 |
+
with open(SRT_PATH, 'wb') as buffer:
|
59 |
+
try:
|
60 |
+
shutil.copyfileobj(srt_file.file, buffer)
|
61 |
+
finally:
|
62 |
+
srt_file.file.close()
|
63 |
+
# Process the video
|
64 |
+
output_path = process_video(temp_input_path, SRT_PATH, max_words_per_line, fontsize, font, bg_color, text_color)
|
65 |
+
return FileResponse(output_path, media_type="video/mp4", filename=f"result_{video_file.filename}")
|
66 |
# Process the video
|
67 |
+
output_path = process_video(temp_input_path, None, max_words_per_line, fontsize, font, bg_color, text_color)
|
68 |
+
# FileResponse(output_path, media_type="text/srt", filename=f"result_{video_file.filename.split('.')[0]}.srt")
|
69 |
# Return the processed video file
|
70 |
+
return FileResponse(output_path, media_type="video/mp4", filename=f"result_{video_file.filename}")
|
71 |
+
|
72 |
except Exception as e:
|
73 |
raise HTTPException(status_code=500, detail=str(e))
|
utils/process_video.py
CHANGED
@@ -1,5 +1,4 @@
|
|
1 |
# Import necessary modules
|
2 |
-
from utils.download_video import download_video
|
3 |
from utils.transcriber import transcriber
|
4 |
from utils.subtitler import subtitler
|
5 |
import logging, os
|
@@ -13,15 +12,21 @@ logging.basicConfig(filename='main.log',
|
|
13 |
|
14 |
# API Function
|
15 |
def process_video(invideo_filename:str,
|
|
|
|
|
16 |
fontsize:str,
|
|
|
17 |
bg_color:str,
|
18 |
-
|
19 |
):
|
|
|
|
|
|
|
|
|
20 |
SRT_PATH = os.path.abspath(f"{invideo_filename.split('.')[0]}.srt")
|
21 |
-
OUTVIDEO_PATH = os.path.join("temp/", f"result.mp4")
|
22 |
if not os.path.exists(SRT_PATH):
|
23 |
transcriber(invideo_filename, SRT_PATH, max_words_per_line)
|
24 |
logging.info("Transcription Complete")
|
25 |
-
subtitler(invideo_filename, SRT_PATH, OUTVIDEO_PATH, fontsize, bg_color)
|
26 |
logging.info("Subtitling Complete")
|
27 |
return OUTVIDEO_PATH
|
|
|
1 |
# Import necessary modules
|
|
|
2 |
from utils.transcriber import transcriber
|
3 |
from utils.subtitler import subtitler
|
4 |
import logging, os
|
|
|
12 |
|
13 |
# API Function
|
14 |
def process_video(invideo_filename:str,
|
15 |
+
srt_path: str,
|
16 |
+
max_words_per_line:int,
|
17 |
fontsize:str,
|
18 |
+
font:str,
|
19 |
bg_color:str,
|
20 |
+
text_color:str
|
21 |
):
|
22 |
+
OUTVIDEO_PATH = os.path.join("temp/", "result.mp4")
|
23 |
+
if srt_path:
|
24 |
+
subtitler(invideo_filename, srt_path, OUTVIDEO_PATH, fontsize, font, bg_color, text_color)
|
25 |
+
return OUTVIDEO_PATH
|
26 |
SRT_PATH = os.path.abspath(f"{invideo_filename.split('.')[0]}.srt")
|
|
|
27 |
if not os.path.exists(SRT_PATH):
|
28 |
transcriber(invideo_filename, SRT_PATH, max_words_per_line)
|
29 |
logging.info("Transcription Complete")
|
30 |
+
subtitler(invideo_filename, SRT_PATH, OUTVIDEO_PATH, fontsize, font, bg_color, text_color)
|
31 |
logging.info("Subtitling Complete")
|
32 |
return OUTVIDEO_PATH
|
utils/subtitler.py
CHANGED
@@ -23,22 +23,25 @@ def parse_srt(srt_file):
|
|
23 |
|
24 |
|
25 |
def subtitler(video_file:str,
|
26 |
-
|
27 |
output_file:str,
|
28 |
fontsize:int,
|
29 |
-
|
|
|
|
|
|
|
30 |
"""Add subtitles from an SRT file to a video."""
|
31 |
video_file = os.path.abspath(video_file)
|
32 |
-
|
33 |
output_file = os.path.abspath(output_file)
|
34 |
|
35 |
clip = VideoFileClip(video_file)
|
36 |
-
subtitles = parse_srt(
|
37 |
|
38 |
subtitle_clips = []
|
39 |
for start, end, text in subtitles:
|
40 |
# Create TextClip with specified styling
|
41 |
-
txt_clip = TextClip(text, fontsize=fontsize, color=
|
42 |
bg_color=bg_color, align='center', size=(clip.w*1/2, None))
|
43 |
txt_clip = txt_clip.set_position(('center', 'bottom')).set_duration(clip.duration).set_start(start).set_end(end)
|
44 |
subtitle_x_position = 'center'
|
|
|
23 |
|
24 |
|
25 |
def subtitler(video_file:str,
|
26 |
+
srt_path:str,
|
27 |
output_file:str,
|
28 |
fontsize:int,
|
29 |
+
font: str,
|
30 |
+
bg_color:str,
|
31 |
+
text_color:str
|
32 |
+
):
|
33 |
"""Add subtitles from an SRT file to a video."""
|
34 |
video_file = os.path.abspath(video_file)
|
35 |
+
srt_path = os.path.abspath(srt_path)
|
36 |
output_file = os.path.abspath(output_file)
|
37 |
|
38 |
clip = VideoFileClip(video_file)
|
39 |
+
subtitles = parse_srt(srt_path)
|
40 |
|
41 |
subtitle_clips = []
|
42 |
for start, end, text in subtitles:
|
43 |
# Create TextClip with specified styling
|
44 |
+
txt_clip = TextClip(text, fontsize=fontsize, color=text_color, font=font, method='caption',
|
45 |
bg_color=bg_color, align='center', size=(clip.w*1/2, None))
|
46 |
txt_clip = txt_clip.set_position(('center', 'bottom')).set_duration(clip.duration).set_start(start).set_end(end)
|
47 |
subtitle_x_position = 'center'
|
utils/transcriber.py
CHANGED
@@ -58,6 +58,4 @@ def transcriber(input_path:str,
|
|
58 |
|
59 |
logging.info("Detected language '%s' with probability %f" % (info.language, info.language_probability))
|
60 |
logging.info("Writing file...")
|
61 |
-
write_srt(segments=segments, srt_path=srt_path, max_words_per_line=max_words_per_line)
|
62 |
-
|
63 |
-
|
|
|
58 |
|
59 |
logging.info("Detected language '%s' with probability %f" % (info.language, info.language_probability))
|
60 |
logging.info("Writing file...")
|
61 |
+
write_srt(segments=segments, srt_path=srt_path, max_words_per_line=max_words_per_line)
|
|
|
|