Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
W
wmdigit_video_cut
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
wmdigit_video_cut
Commits
5409f5a0
Commit
5409f5a0
authored
Nov 09, 2023
by
Administrator
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
基本完成
parent
2c8d4c77
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
296 additions
and
41 deletions
+296
-41
.dockerignore
.dockerignore
+0
-1
Dockerfile
Dockerfile
+1
-1
Dockerfile_v0.2
Dockerfile_v0.2
+1
-1
README.md
README.md
+16
-15
util.py
app/utils/util.py
+21
-0
utils.py
app/video_cut/autocut/utils.py
+6
-4
whisper_model.py
app/video_cut/autocut/whisper_model.py
+1
-0
wmdigit_cut.py
app/video_cut/autocut/wmdigit_cut.py
+2
-1
wmdigit_transcribe.py
app/video_cut/autocut/wmdigit_transcribe.py
+3
-4
load_args.py
app/video_cut/load_args.py
+138
-0
main.py
app/video_cut/main.py
+70
-0
docker-compose-v1.yml
docker-compose-v1.yml
+1
-1
docker-compose.yml
docker-compose.yml
+1
-1
gunicorn.conf.py
gunicorn.conf.py
+1
-5
start.py
start.py
+34
-7
No files found.
.dockerignore
View file @
5409f5a0
...
...
@@ -6,4 +6,3 @@ outputs
tests
**/checkpoints
/app/ai_gen_image/images
\ No newline at end of file
Dockerfile
View file @
5409f5a0
FROM
harbor.5jstore.com:8020/ai/wm_
generate_ai
:v0.2
FROM
harbor.5jstore.com:8020/ai/wm_
video_cut
:v0.2
LABEL
maintainer="zhouchengbo@wmdigit.com"
ARG
DEBIAN_FRONTEND=noninteractive
...
...
Dockerfile_v0.2
View file @
5409f5a0
FROM harbor.5jstore.com:8020/ai/wm_
generate_ai
:v0.1
FROM harbor.5jstore.com:8020/ai/wm_
video_cut
:v0.1
LABEL maintainer="zhouchengbo@wmdigit.com"
ARG DEBIAN_FRONTEND=noninteractive
...
...
README.md
View file @
5409f5a0
# AI Generate
# 视频自动翻译剪辑
# 生成基础镜像,将所有依赖都放到基础镜像中
<!-- v0 -->
docker rmi harbor.5jstore.com:8020/ai/wm_
generate_ai
:v0
docker build -f Dockerfile_v0 -t harbor.5jstore.com:8020/ai/wm_
generate_ai
:v0 .
docker rmi harbor.5jstore.com:8020/ai/wm_
video_cut
:v0
docker build -f Dockerfile_v0 -t harbor.5jstore.com:8020/ai/wm_
video_cut
:v0 .
<!-- 0.1 根据v0手工生成,主要安装完整版的 cuda -->
docker rmi harbor.5jstore.com:8020/ai/wm_
generate_ai
:v0.1
docker run --gpus all --runtime=nvidia -it harbor.5jstore.com:8020/ai/wm_
generate_ai
:v0 /bin/bash
docker rmi harbor.5jstore.com:8020/ai/wm_
video_cut
:v0.1
docker run --gpus all --runtime=nvidia -it harbor.5jstore.com:8020/ai/wm_
video_cut
:v0 /bin/bash
wget https://developer.download.nvidia.com/compute/cuda/11.3.0/local_installers/cuda_11.3.0_465.19.01_linux.run
sh cuda_11.3.0_465.19.01_linux.run
1、不选驱动
...
...
@@ -21,16 +20,16 @@ export CUDA_HOME=/usr/local/cuda-11.3
删除下载文件
rm cuda_11.3.0_465.19.01_linux.run
提交新镜像
docker commit c9c2a347491d harbor.5jstore.com:8020/ai/wm_
generate_ai
:v0.1
docker commit c9c2a347491d harbor.5jstore.com:8020/ai/wm_
video_cut
:v0.1
<!-- end -->
<!-- 0.2 在0.1基础上安装项目依赖 -->
docker rmi harbor.5jstore.com:8020/ai/wm_
generate_ai
:v0.2
docker build -f Dockerfile_v0.2 -t harbor.5jstore.com:8020/ai/wm_
generate_ai
:v0.2 .
docker rmi harbor.5jstore.com:8020/ai/wm_
video_cut
:v0.2
docker build -f Dockerfile_v0.2 -t harbor.5jstore.com:8020/ai/wm_
video_cut
:v0.2 .
# 生成主镜像
docker rmi harbor.5jstore.com:8020/ai/wm_
generate_ai
:v2
docker build -f Dockerfile -t harbor.5jstore.com:8020/ai/wm_
generate_ai
:v2 .
docker rmi harbor.5jstore.com:8020/ai/wm_
video_cut
:v2
docker build -f Dockerfile -t harbor.5jstore.com:8020/ai/wm_
video_cut
:v2 .
docker-compose up -d
...
...
@@ -42,9 +41,9 @@ docker-compose up -d
<!-- 调试docker镜像,去掉Dockerfile里最后一句启动命令 -->
docker run -it harbor.5jstore.com:8020/ai/wm_
generate_ai
:v0 /bin/bash
docker run -it harbor.5jstore.com:8020/ai/wm_
generate_ai
:v1 /bin/bash
docker run -it harbor.5jstore.com:8020/ai/wm_
generate_ai
:v2 /bin/bash
docker run -it harbor.5jstore.com:8020/ai/wm_
video_cut
:v0 /bin/bash
docker run -it harbor.5jstore.com:8020/ai/wm_
video_cut
:v1 /bin/bash
docker run -it harbor.5jstore.com:8020/ai/wm_
video_cut
:v2 /bin/bash
docker run --gpus all --runtime=nvidia -v ./inputs/:/app/inputs/ -v ./outputs/:/app/outputs/ -it harbor.5jstore.com:8020/common/ai_generate_video:proc_v4 /bin/bash
...
...
@@ -52,4 +51,6 @@ ffmpeg -hwaccels
<!-- 调试命令 -->
python start.py
gunicorn start:app -c ./gunicorn.conf.py
\ No newline at end of file
gunicorn start:app -c ./gunicorn.conf.py
flask --app start run --debug
\ No newline at end of file
app/utils/util.py
View file @
5409f5a0
...
...
@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
import
requests
from
werkzeug.utils
import
secure_filename
from
urllib
import
parse
import
oss2
import
datetime
,
os
...
...
@@ -134,3 +135,23 @@ def get_wm_option(wm_key):
return
""
else
:
return
wm_option
.
option_value
def
upload_file
(
file
):
upload_dir
=
f
'./inputs/{datetime.datetime.now().strftime("
%
Y
%
m
%
d
%
H
%
M
%
S")}'
if
not
os
.
path
.
exists
(
upload_dir
):
os
.
makedirs
(
upload_dir
)
def
allowed_file
(
filename
):
ALLOWED_EXTENSIONS
=
{
'txt'
,
'pdf'
,
'png'
,
'jpg'
,
'jpeg'
,
'gif'
,
'mp4'
}
return
'.'
in
filename
and
\
filename
.
rsplit
(
'.'
,
1
)[
1
]
.
lower
()
in
ALLOWED_EXTENSIONS
if
file
and
allowed_file
(
file
.
filename
):
filename
=
secure_filename
(
file
.
filename
)
filepath
=
os
.
path
.
join
(
upload_dir
,
filename
)
file
.
save
(
filepath
)
return
filepath
else
:
return
''
app/video_cut/autocut/utils.py
View file @
5409f5a0
...
...
@@ -412,10 +412,12 @@ def text_to_audio_by_edge_tts(text, local_voice_file, lang, retry=1):
def
get_mp3_duration
(
file_path
):
from
mutagen.mp3
import
MP3
audio
=
MP3
(
file_path
)
duration_in_seconds
=
audio
.
info
.
length
return
duration_in_seconds
try
:
audio
=
MP3
(
file_path
)
duration_in_seconds
=
audio
.
info
.
length
return
duration_in_seconds
except
:
return
0
def
delete_files
(
current_dir
,
prefix
):
...
...
app/video_cut/autocut/whisper_model.py
View file @
5409f5a0
...
...
@@ -50,6 +50,7 @@ class WhisperModel(AbstractWhisperModel):
"tiny"
,
"base"
,
"small"
,
"medium"
,
"large"
,
"large-v2"
]
=
"small"
,
device
:
Union
[
Literal
[
"cpu"
,
"cuda"
],
None
]
=
None
,
env
=
"dev"
,
):
self
.
device
=
device
...
...
app/video_cut/autocut/wmdigit_cut.py
View file @
5409f5a0
...
...
@@ -132,4 +132,5 @@ class Cutter:
# utils.delete_files(os.path.dirname(fns['media']), f"._{os.path.splitext(os.path.basename(fns['media']))[0]}")
# utils.delete_files(os.path.dirname(fns['media']), f".DS_Store")
# utils.delete_files(os.path.dirname(fns['media']), f"._.DS_Store")
utils
.
delete_files
(
os
.
path
.
dirname
(
fns
[
'media'
]),
f
"."
)
\ No newline at end of file
utils
.
delete_files
(
os
.
path
.
dirname
(
fns
[
'media'
]),
f
"."
)
return
output_fn
\ No newline at end of file
app/video_cut/autocut/wmdigit_transcribe.py
View file @
5409f5a0
...
...
@@ -54,16 +54,15 @@ class Transcribe:
logging
.
info
(
f
"Transcribed {input} to {output}"
)
self
.
_save_md
(
name
+
".md"
,
output
,
input
,
bool
(
self
.
args
.
wmdigit
))
logging
.
info
(
f
'Saved texts to {name + ".md"} to mark sentences'
)
except
:
if
retry
==
10
:
raise
RuntimeError
(
f
"Failed to Transcribing {
input
}"
)
except
Exception
as
e
:
if
retry
==
3
:
raise
RuntimeError
(
f
"Failed to Transcribing {
e
}"
)
else
:
time
.
sleep
(
1
)
logging
.
info
(
f
"Retry {retry} to Transcribing {input}"
)
retry
+=
1
self
.
run
(
retry
)
def
_detect_voice_activity
(
self
,
audio
)
->
List
[
SPEECH_ARRAY_INDEX
]:
"""Detect segments that have voice activities"""
if
self
.
args
.
vad
==
"0"
:
...
...
app/video_cut/load_args.py
0 → 100644
View file @
5409f5a0
import
argparse
import
logging
import
os
from
app.video_cut.autocut
import
utils
from
app.video_cut.autocut.type
import
WhisperMode
,
WhisperModel
def
main_args
(
logger
):
logger
.
info
(
'load augument'
)
parser
=
argparse
.
ArgumentParser
()
parser
.
add_argument
(
"--inputs"
,
type
=
str
,
help
=
"Inputs filenames/folders"
)
parser
.
add_argument
(
"--lang"
,
type
=
str
,
default
=
"zh"
,
choices
=
[
"zh"
,
"en"
,
"Afrikaans"
,
"Arabic"
,
"Armenian"
,
"Azerbaijani"
,
"Belarusian"
,
"Bosnian"
,
"Bulgarian"
,
"Catalan"
,
"Croatian"
,
"Czech"
,
"Danish"
,
"Dutch"
,
"Estonian"
,
"Finnish"
,
"French"
,
"Galician"
,
"German"
,
"Greek"
,
"Hebrew"
,
"Hindi"
,
"Hungarian"
,
"Icelandic"
,
"Indonesian"
,
"Italian"
,
"Japanese"
,
"Kannada"
,
"Kazakh"
,
"Korean"
,
"Latvian"
,
"Lithuanian"
,
"Macedonian"
,
"Malay"
,
"Marathi"
,
"Maori"
,
"Nepali"
,
"Norwegian"
,
"Persian"
,
"Polish"
,
"Portuguese"
,
"Romanian"
,
"Russian"
,
"Serbian"
,
"Slovak"
,
"Slovenian"
,
"Spanish"
,
"Swahili"
,
"Swedish"
,
"Tagalog"
,
"Tamil"
,
"Thai"
,
"Turkish"
,
"Ukrainian"
,
"Urdu"
,
"Vietnamese"
,
"Welsh"
,
],
help
=
"The output language of transcription"
,
)
parser
.
add_argument
(
"--prompt"
,
type
=
str
,
default
=
""
,
help
=
"initial prompt feed into whisper"
)
parser
.
add_argument
(
"--whisper-mode"
,
type
=
str
,
default
=
WhisperMode
.
WHISPER
.
value
,
choices
=
WhisperMode
.
get_values
(),
help
=
"Whisper inference mode: whisper: run whisper locally; openai: use openai api."
,
)
parser
.
add_argument
(
"--openai-rpm"
,
type
=
int
,
default
=
3
,
choices
=
[
3
,
50
],
help
=
"Openai Whisper API REQUESTS PER MINUTE(FREE USERS: 3RPM; PAID USERS: 50RPM). "
"More info: https://platform.openai.com/docs/guides/rate-limits/overview"
,
)
parser
.
add_argument
(
"--whisper-model"
,
type
=
str
,
default
=
WhisperModel
.
SMALL
.
value
,
choices
=
WhisperModel
.
get_values
(),
help
=
"The whisper model used to transcribe."
,
)
parser
.
add_argument
(
"--bitrate"
,
type
=
str
,
default
=
"10m"
,
help
=
"The bitrate to export the cutted video, such as 10m, 1m, or 500k"
,
)
parser
.
add_argument
(
"--vad"
,
help
=
"If or not use VAD"
,
choices
=
[
"1"
,
"0"
,
"auto"
],
default
=
"0"
)
parser
.
add_argument
(
"--force"
,
help
=
"Force write even if files exist"
,
action
=
argparse
.
BooleanOptionalAction
,
)
parser
.
add_argument
(
"--encoding"
,
type
=
str
,
default
=
"utf-8"
,
help
=
"Document encoding format"
)
parser
.
add_argument
(
"--device"
,
type
=
str
,
default
=
None
,
choices
=
[
"cpu"
,
"cuda"
],
help
=
"Force to CPU or GPU for transcribing. In default automatically use GPU if available."
,
)
parser
.
add_argument
(
"--wmdigit"
,
help
=
"Convert video to different language"
,
action
=
argparse
.
BooleanOptionalAction
,
)
args
=
parser
.
parse_args
(
args
=
[])
args
.
wmdigit
=
True
args
.
force
=
True
args
.
vad
=
"0"
return
args
\ No newline at end of file
app/video_cut/main.py
0 → 100644
View file @
5409f5a0
from
flask
import
abort
,
request
,
jsonify
import
datetime
,
os
,
time
from
app.video_cut.autocut
import
wmdigit_cut
,
wmdigit_transcribe
,
utils
def
error
(
msg
):
abort
(
jsonify
({
"error"
:
msg
}))
# 校验请求参数
def
validate_request
():
if
not
request
.
json
or
'instances'
not
in
request
.
json
:
error
(
"参数错误: 缺少instances参数"
)
instance
=
request
.
json
[
'instances'
]
if
len
(
instance
)
<=
0
or
'video'
not
in
instance
[
0
]
or
'lang'
not
in
instance
[
0
]:
error
(
'参数错误: instances缺少:video,lang'
)
video
=
instance
[
0
][
'video'
]
lang
=
instance
[
0
][
'lang'
]
if
len
(
video
)
<=
0
:
error
(
'参数错误: video 参数不可为空'
)
if
len
(
lang
)
<=
0
:
error
(
'参数错误: lang 参数不可为空'
)
# if not video.startswith('http'):
# error('video 必须是网络路径')
return
video
,
lang
# 主线
def
video_cut_pipeline
(
logger
,
args
):
# print(args)
time_record
=
[]
media_file
,
lang
=
validate_request
()
all_start_time
=
time
.
time
()
# 1、视频生成srt和md
start_time
=
time
.
time
()
srt_fn
=
utils
.
change_ext
(
media_file
,
"srt"
)
md_fn
=
utils
.
change_ext
(
media_file
,
"md"
)
# 如果目标语言不是中文,则提示whisper翻译全部字幕
if
lang
!=
"zh"
:
prompt
=
f
"Subtitles must be fully translated into {lang}"
else
:
prompt
=
""
logger
.
info
(
f
"Transcribe {media_file} lang={lang} promt={prompt}"
)
args
.
inputs
=
[
media_file
]
args
.
lang
=
lang
wmdigit_transcribe
.
Transcribe
(
args
)
.
run
()
time_record
.
append
(
f
"视频生成srt和md。耗时: {time.time() - start_time:.4f} 秒"
)
# 2、从字幕生成cut视频
start_time
=
time
.
time
()
args
.
inputs
=
[
media_file
,
md_fn
,
srt_fn
]
final_video_fn
=
wmdigit_cut
.
Cutter
(
args
)
.
run
()
time_record
.
append
(
f
"从字幕生成cut视频。耗时: {time.time() - start_time:.4f} 秒"
)
time_record
.
append
(
f
"所有步骤处理完毕。耗时: {time.time() - all_start_time:.4f} 秒"
)
for
i
in
time_record
:
print
(
i
)
return
final_video_fn
,
srt_fn
docker-compose-v1.yml
View file @
5409f5a0
version
:
'
3'
services
:
pytorch
:
image
:
"
harbor.5jstore.com:8020/ai/wm_
generate_ai
:v1"
image
:
"
harbor.5jstore.com:8020/ai/wm_
video_cut
:v1"
restart
:
always
runtime
:
nvidia
environment
:
...
...
docker-compose.yml
View file @
5409f5a0
version
:
'
3'
services
:
pytorch
:
image
:
"
harbor.5jstore.com:8020/ai/wm_
generate_ai
:v2"
image
:
"
harbor.5jstore.com:8020/ai/wm_
video_cut
:v2"
restart
:
always
runtime
:
nvidia
environment
:
...
...
gunicorn.conf.py
View file @
5409f5a0
...
...
@@ -2,8 +2,4 @@ workers = 1 # 定义同时开启的处理请求的进程数量,根据网站
worker_class
=
"gevent"
# 采用gevent库,支持异步处理请求,提高吞吐量
timeout
=
600
bind
=
"0.0.0.0:5000"
accesslog
=
'-'
errorlog
=
'-'
loglevel
=
'debug'
\ No newline at end of file
bind
=
"0.0.0.0:8080"
start.py
View file @
5409f5a0
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from
flask
import
Flask
,
abort
,
request
,
jsonify
from
flask
import
Flask
,
abort
,
request
,
jsonify
,
send_file
import
datetime
,
os
,
sys
,
time
import
logging
from
app.utils
import
util
from
app.models.wm_option
import
db
from
app.video_cut.load_args
import
main_args
from
app.video_cut.main
import
video_cut_pipeline
logging
.
basicConfig
(
level
=
logging
.
INFO
,
format
=
'
%(asctime)
s -
%(name)
s -
%(levelname)
s -
%(message)
s'
)
logger
=
logging
.
getLogger
(
__name__
)
...
...
@@ -24,12 +26,37 @@ input_root = os.path.join(root, 'inputs')
output_root
=
os
.
path
.
join
(
root
,
'outputs'
)
# 预加载模型
args
=
main_args
(
logger
)
# 对外接口
@
app
.
route
(
'/ai_generate_video'
,
methods
=
[
'GET'
])
def
ai_generate_video
():
return
jsonify
({
"result"
:
"akakkakaka"
})
@
app
.
route
(
'/wm_video_cut'
,
methods
=
[
'POST'
])
def
wm_video_cut
():
final_video_url
,
srt_url
=
video_cut_pipeline
(
logger
,
args
)
return
jsonify
({
"result"
:
{
"final_video_url"
:
final_video_url
,
"srt_url"
:
srt_url
}})
@
app
.
route
(
'/upload_file'
,
methods
=
[
'POST'
])
def
upload_file
():
# check if the post request has the file part
if
'file'
not
in
request
.
files
:
return
'No file part'
,
403
file
=
request
.
files
[
'file'
]
if
file
.
filename
==
''
:
return
'No selected file'
,
403
file_url
=
util
.
upload_file
(
file
)
if
not
file_url
:
return
'上传文件失败'
,
500
else
:
return
jsonify
({
"result"
:
{
"file_url"
:
file_url
}})
@
app
.
route
(
'/download/<path:file_path>'
)
def
download_file
(
file_path
):
if
not
file_path
.
startswith
(
'input'
):
return
'文件不存在'
,
404
# 发送文件给浏览器进行下载
return
send_file
(
file_path
,
as_attachment
=
True
)
if
__name__
==
"__main__"
:
# 将host设置为0.0.0.0,则外网用户也可以访问到这个服务
app
.
run
(
debug
=
True
,
host
=
"0.0.0.0"
,
use_reloader
=
False
)
app
.
run
()
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment