|
@@ -68,26 +68,29 @@ class FFmpeg():
|
|
|
@classmethod
|
|
|
def video_crop(cls, video_path, file_path):
|
|
|
crop_url = file_path + 'crop.mp4'
|
|
|
- # 获取视频的原始宽高信息
|
|
|
- ffprobe_cmd = cls.asyncio_run_subprocess(
|
|
|
- ["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=width,height", "-of",
|
|
|
- "csv=p=0", video_path], timeout=10)
|
|
|
- width, height = map(int, ffprobe_cmd.strip().split(','))
|
|
|
- # 计算裁剪后的高度
|
|
|
- new_height = int(height * 0.8)
|
|
|
+ try:
|
|
|
+ # 获取视频的原始宽高信息
|
|
|
+ ffprobe_cmd = cls.asyncio_run_subprocess(
|
|
|
+ ["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=width,height", "-of",
|
|
|
+ "csv=p=0", video_path], timeout=10)
|
|
|
+ width, height = map(int, ffprobe_cmd.strip().split(','))
|
|
|
+ # 计算裁剪后的高度
|
|
|
+ new_height = int(height * 0.8)
|
|
|
|
|
|
- # 构建 FFmpeg 命令,裁剪视频高度为原始高度的80%
|
|
|
- cls.asyncio_run_subprocess(
|
|
|
- [
|
|
|
- "ffmpeg",
|
|
|
- "-i", video_path,
|
|
|
- "-vf", f"crop={width}:{new_height}",
|
|
|
- "-c:v", "libx264",
|
|
|
- "-c:a", "aac",
|
|
|
- "-y",
|
|
|
- crop_url
|
|
|
- ],timeout=240)
|
|
|
- return crop_url
|
|
|
+ # 构建 FFmpeg 命令,裁剪视频高度为原始高度的80%
|
|
|
+ cls.asyncio_run_subprocess(
|
|
|
+ [
|
|
|
+ "ffmpeg",
|
|
|
+ "-i", video_path,
|
|
|
+ "-vf", f"crop={width}:{new_height}",
|
|
|
+ "-c:v", "libx264",
|
|
|
+ "-c:a", "aac",
|
|
|
+ "-y",
|
|
|
+ crop_url
|
|
|
+ ],timeout=240)
|
|
|
+ return crop_url
|
|
|
+ except Exception as e:
|
|
|
+ return crop_url
|
|
|
|
|
|
"""
|
|
|
视频截断
|
|
@@ -96,35 +99,39 @@ class FFmpeg():
|
|
|
def video_ggduration(cls, video_path, file_path, gg_duration_total):
|
|
|
gg_duration_url = file_path + 'gg_duration.mp4'
|
|
|
# 获取视频时长
|
|
|
- total_duration = cls.get_video_duration(video_path)
|
|
|
- if total_duration == 0:
|
|
|
+ try:
|
|
|
+ total_duration = cls.get_video_duration(video_path)
|
|
|
+ if total_duration == 0:
|
|
|
+ return gg_duration_url
|
|
|
+ duration = int(total_duration) - int(gg_duration_total)
|
|
|
+ if int(total_duration) < int(gg_duration_total):
|
|
|
+ return gg_duration_url
|
|
|
+ cls.asyncio_run_subprocess([
|
|
|
+ "ffmpeg",
|
|
|
+ "-i", video_path,
|
|
|
+ "-c:v", "libx264",
|
|
|
+ "-c:a", "aac",
|
|
|
+ "-t", str(duration),
|
|
|
+ "-y",
|
|
|
+ gg_duration_url
|
|
|
+ ], timeout= 360)
|
|
|
return gg_duration_url
|
|
|
- duration = int(total_duration) - int(gg_duration_total)
|
|
|
- if int(total_duration) < int(gg_duration_total):
|
|
|
+ except Exception as e:
|
|
|
return gg_duration_url
|
|
|
- cls.asyncio_run_subprocess([
|
|
|
- "ffmpeg",
|
|
|
- "-i", video_path,
|
|
|
- "-c:v", "libx264",
|
|
|
- "-c:a", "aac",
|
|
|
- "-t", str(duration),
|
|
|
- "-y",
|
|
|
- gg_duration_url
|
|
|
- ], timeout= 360)
|
|
|
- return gg_duration_url
|
|
|
|
|
|
"""
|
|
|
截取原视频最后一帧
|
|
|
"""
|
|
|
@classmethod
|
|
|
def video_png(cls, video_path, file_path):
|
|
|
- """
|
|
|
- """
|
|
|
# 获取视频的原始宽高信息
|
|
|
jpg_url = file_path + 'png.jpg'
|
|
|
- cls.asyncio_run_subprocess(
|
|
|
- ["ffmpeg", "-sseof", "-1", '-i', video_path, '-frames:v', '1', "-y", jpg_url], timeout=120)
|
|
|
- return jpg_url
|
|
|
+ try:
|
|
|
+ cls.asyncio_run_subprocess(
|
|
|
+ ["ffmpeg", "-sseof", "-1", '-i', video_path, '-frames:v', '1', "-y", jpg_url], timeout=120)
|
|
|
+ return jpg_url
|
|
|
+ except Exception as e:
|
|
|
+ return jpg_url
|
|
|
|
|
|
"""
|
|
|
获取视频音频
|
|
@@ -132,36 +139,48 @@ class FFmpeg():
|
|
|
@classmethod
|
|
|
def get_video_mp3(cls, video_file, video_path_url, pw_random_id):
|
|
|
pw_mp3_path = video_path_url + str(pw_random_id) +'pw_video.mp3'
|
|
|
- cls.asyncio_run_subprocess([
|
|
|
- 'ffmpeg',
|
|
|
- '-i', video_file,
|
|
|
- '-q:a', '0',
|
|
|
- '-map', 'a',
|
|
|
- pw_mp3_path
|
|
|
- ], timeout=120)
|
|
|
- time.sleep(1)
|
|
|
- return pw_mp3_path
|
|
|
+ try:
|
|
|
+ cls.asyncio_run_subprocess([
|
|
|
+ 'ffmpeg',
|
|
|
+ '-i', video_file,
|
|
|
+ '-q:a', '0',
|
|
|
+ '-map', 'a',
|
|
|
+ pw_mp3_path
|
|
|
+ ], timeout=120)
|
|
|
+ time.sleep(1)
|
|
|
+ return pw_mp3_path
|
|
|
+ except Exception as e:
|
|
|
+ return pw_mp3_path
|
|
|
|
|
|
"""横屏视频改为竖屏"""
|
|
|
@classmethod
|
|
|
def update_video_h_w(cls, video_path, file_path):
|
|
|
video_h_w_path = file_path +'video_h_w_video.mp4'
|
|
|
- cls.asyncio_run_subprocess(["ffmpeg" ,"-i" ,video_path ,"-vf" ,"scale=640:ih*640/iw,pad=iw:iw*16/9:(ow-iw)/2:(oh-ih)/2" ,video_h_w_path],timeout=420)
|
|
|
- return video_h_w_path
|
|
|
+ try:
|
|
|
+ cls.asyncio_run_subprocess(["ffmpeg" ,"-i" ,video_path ,"-vf" ,"scale=640:ih*640/iw,pad=iw:iw*16/9:(ow-iw)/2:(oh-ih)/2" ,video_h_w_path],timeout=420)
|
|
|
+ return video_h_w_path
|
|
|
+ except Exception as e:
|
|
|
+ return video_h_w_path
|
|
|
|
|
|
"""视频转为640像素"""
|
|
|
@classmethod
|
|
|
def video_640(cls, video_path, file_path):
|
|
|
video_url = file_path + 'pixelvideo.mp4'
|
|
|
- cls.asyncio_run_subprocess(["ffmpeg" ,"-i" ,video_path ,"-vf" ,"scale=360:640" ,video_url],timeout=420)
|
|
|
- return video_url
|
|
|
+ try:
|
|
|
+ cls.asyncio_run_subprocess(["ffmpeg" ,"-i" ,video_path ,"-vf" ,"scale=360:640" ,video_url],timeout=420)
|
|
|
+ return video_url
|
|
|
+ except Exception as e:
|
|
|
+ return video_url
|
|
|
|
|
|
"""视频拼接到一起"""
|
|
|
@classmethod
|
|
|
def h_b_video(cls, video_path, pw_path, file_path):
|
|
|
video_url = file_path + 'hbvideo.mp4'
|
|
|
- cls.asyncio_run_subprocess(["ffmpeg","-i", video_path, "-i", pw_path, "-filter_complex" ,"[0:v]scale=360:640[v1]; [1:v]scale=360:640[v2]; [v1][0:a][v2][1:a]concat=n=2:v=1:a=1[outv][outa]" ,"-map" ,"[outv]" ,"-map" ,"[outa]" ,video_url],timeout=500)
|
|
|
- return video_url
|
|
|
+ try:
|
|
|
+ cls.asyncio_run_subprocess(["ffmpeg","-i", video_path, "-i", pw_path, "-filter_complex" ,"[0:v]scale=360:640[v1]; [1:v]scale=360:640[v2]; [v1][0:a][v2][1:a]concat=n=2:v=1:a=1[outv][outa]" ,"-map" ,"[outv]" ,"-map" ,"[outa]" ,video_url],timeout=500)
|
|
|
+ return video_url
|
|
|
+ except Exception as e:
|
|
|
+ return video_url
|
|
|
|
|
|
"""横屏视频顶部增加字幕"""
|
|
|
@classmethod
|
|
@@ -169,31 +188,34 @@ class FFmpeg():
|
|
|
single_video_srt = video_path_url + str(pw_random_id) +'video_zm.srt'
|
|
|
single_video_txt = video_path_url + str(pw_random_id) +'video_zm.txt'
|
|
|
single_video = video_path_url + str(pw_random_id) +'video_zm.mp4'
|
|
|
- duration = cls.get_video_duration(new_video_path)
|
|
|
- if duration == 0:
|
|
|
- return new_video_path
|
|
|
- start_time = cls.seconds_to_srt_time(0)
|
|
|
- end_time = cls.seconds_to_srt_time(duration)
|
|
|
- # zm = '致敬伟大的教员,为整个民族\n感谢老人家历史向一代伟人'
|
|
|
- with open(single_video_txt, 'w') as f:
|
|
|
- f.write(f"file '{new_video_path}'\n")
|
|
|
- with open(single_video_srt, 'w') as f:
|
|
|
- f.write(f"1\n{start_time} --> {end_time}\n{new_text}\n\n")
|
|
|
- subtitle_cmd = f"subtitles={single_video_srt}:force_style='Fontsize=12,Fontname=wqy-zenhei,Outline=2,PrimaryColour=&H00FFFF,SecondaryColour=&H000000,Bold=1,MarginV=225'"
|
|
|
- draw = f"{subtitle_cmd}"
|
|
|
- cls.asyncio_run_subprocess([
|
|
|
- "ffmpeg",
|
|
|
- "-f", "concat",
|
|
|
- "-safe", "0",
|
|
|
- "-i", single_video_txt,
|
|
|
- "-c:v", "libx264",
|
|
|
- "-c:a", "aac",
|
|
|
- "-vf", draw,
|
|
|
- "-y",
|
|
|
- single_video
|
|
|
- ],timeout=500)
|
|
|
- # subprocess.run(ffmpeg_cmd)
|
|
|
- return single_video
|
|
|
+ try:
|
|
|
+ duration = cls.get_video_duration(new_video_path)
|
|
|
+ if duration == 0:
|
|
|
+ return new_video_path
|
|
|
+ start_time = cls.seconds_to_srt_time(0)
|
|
|
+ end_time = cls.seconds_to_srt_time(duration)
|
|
|
+ # zm = '致敬伟大的教员,为整个民族\n感谢老人家历史向一代伟人'
|
|
|
+ with open(single_video_txt, 'w') as f:
|
|
|
+ f.write(f"file '{new_video_path}'\n")
|
|
|
+ with open(single_video_srt, 'w') as f:
|
|
|
+ f.write(f"1\n{start_time} --> {end_time}\n{new_text}\n\n")
|
|
|
+ subtitle_cmd = f"subtitles={single_video_srt}:force_style='Fontsize=12,Fontname=wqy-zenhei,Outline=2,PrimaryColour=&H00FFFF,SecondaryColour=&H000000,Bold=1,MarginV=225'"
|
|
|
+ draw = f"{subtitle_cmd}"
|
|
|
+ cls.asyncio_run_subprocess([
|
|
|
+ "ffmpeg",
|
|
|
+ "-f", "concat",
|
|
|
+ "-safe", "0",
|
|
|
+ "-i", single_video_txt,
|
|
|
+ "-c:v", "libx264",
|
|
|
+ "-c:a", "aac",
|
|
|
+ "-vf", draw,
|
|
|
+ "-y",
|
|
|
+ single_video
|
|
|
+ ],timeout=500)
|
|
|
+ # subprocess.run(ffmpeg_cmd)
|
|
|
+ return single_video
|
|
|
+ except Exception as e:
|
|
|
+ return single_video
|
|
|
|
|
|
"""获取mp3时长"""
|
|
|
@classmethod
|
|
@@ -223,34 +245,37 @@ class FFmpeg():
|
|
|
with open(pw_srt_path, 'w') as f:
|
|
|
f.write(pw_srt)
|
|
|
pw_url_path = file_path + 'pw_video.mp4'
|
|
|
- pw_duration = cls.get_mp3_duration(pw_mp3_path)
|
|
|
- if pw_duration == 0:
|
|
|
- return pw_url_path
|
|
|
- time.sleep(2)
|
|
|
- # 添加字幕 wqy-zenhei Hiragino Sans GB
|
|
|
- height = 1080
|
|
|
- margin_v = int(height) // 8 # 可根据需要调整字幕和背景之间的距离
|
|
|
- subtitle_cmd = f"subtitles={pw_srt_path}:force_style='Fontsize=13,Fontname=wqy-zenhei,Outline=0,PrimaryColour=&H000000,SecondaryColour=&H000000,Bold=1,MarginV={margin_v}'"
|
|
|
- bg_position_offset = (int(360) - 360//8) / 1.75
|
|
|
- background_cmd = f"drawbox=y=(ih-{int(360)}/2-{bg_position_offset}):color=yellow@1.0:width=iw:height={int(360)}/4:t=fill"
|
|
|
+ try:
|
|
|
+ pw_duration = cls.get_mp3_duration(pw_mp3_path)
|
|
|
+ if pw_duration == 0:
|
|
|
+ return pw_url_path
|
|
|
+ time.sleep(2)
|
|
|
+ # 添加字幕 wqy-zenhei Hiragino Sans GB
|
|
|
+ height = 1080
|
|
|
+ margin_v = int(height) // 8 # 可根据需要调整字幕和背景之间的距离
|
|
|
+ subtitle_cmd = f"subtitles={pw_srt_path}:force_style='Fontsize=13,Fontname=wqy-zenhei,Outline=0,PrimaryColour=&H000000,SecondaryColour=&H000000,Bold=1,MarginV={margin_v}'"
|
|
|
+ bg_position_offset = (int(360) - 360//8) / 1.75
|
|
|
+ background_cmd = f"drawbox=y=(ih-{int(360)}/2-{bg_position_offset}):color=yellow@1.0:width=iw:height={int(360)}/4:t=fill"
|
|
|
|
|
|
- cls.asyncio_run_subprocess([
|
|
|
- 'ffmpeg',
|
|
|
- '-loop', '1',
|
|
|
- '-i', jpg_path, # 输入的图片文件
|
|
|
- '-i', pw_mp3_path, # 输入的音频文件
|
|
|
- '-c:v', 'libx264', # 视频编码格式
|
|
|
- '-t', str(pw_duration), # 输出视频的持续时间,与音频持续时间相同
|
|
|
- '-pix_fmt', 'yuv420p', # 像素格式
|
|
|
- '-c:a', 'aac', # 音频编码格式
|
|
|
- '-strict', 'experimental', # 使用实验性编码器
|
|
|
- '-shortest', # 确保输出视频的长度与音频一致
|
|
|
- '-vf', f"{background_cmd},{subtitle_cmd}", # 视频过滤器,设置分辨率和其他过滤器
|
|
|
- pw_url_path # 输出的视频文件路径
|
|
|
- ], timeout=500)
|
|
|
- if os.path.exists(pw_srt_path):
|
|
|
- os.remove(pw_srt_path)
|
|
|
- return pw_url_path
|
|
|
+ cls.asyncio_run_subprocess([
|
|
|
+ 'ffmpeg',
|
|
|
+ '-loop', '1',
|
|
|
+ '-i', jpg_path, # 输入的图片文件
|
|
|
+ '-i', pw_mp3_path, # 输入的音频文件
|
|
|
+ '-c:v', 'libx264', # 视频编码格式
|
|
|
+ '-t', str(pw_duration), # 输出视频的持续时间,与音频持续时间相同
|
|
|
+ '-pix_fmt', 'yuv420p', # 像素格式
|
|
|
+ '-c:a', 'aac', # 音频编码格式
|
|
|
+ '-strict', 'experimental', # 使用实验性编码器
|
|
|
+ '-shortest', # 确保输出视频的长度与音频一致
|
|
|
+ '-vf', f"{background_cmd},{subtitle_cmd}", # 视频过滤器,设置分辨率和其他过滤器
|
|
|
+ pw_url_path # 输出的视频文件路径
|
|
|
+ ], timeout=500)
|
|
|
+ if os.path.exists(pw_srt_path):
|
|
|
+ os.remove(pw_srt_path)
|
|
|
+ return pw_url_path
|
|
|
+ except Exception as e:
|
|
|
+ return pw_url_path
|
|
|
|
|
|
|
|
|
"""
|
|
@@ -261,40 +286,43 @@ class FFmpeg():
|
|
|
single_video_url = file_path + 'single_video.mp4'
|
|
|
single_video_srt = file_path + 'single_video.srt'
|
|
|
# 获取时长
|
|
|
- duration = cls.get_video_duration(video_path)
|
|
|
- if duration == 0:
|
|
|
+ try:
|
|
|
+ duration = cls.get_video_duration(video_path)
|
|
|
+ if duration == 0:
|
|
|
+ return single_video_url
|
|
|
+ start_time = cls.seconds_to_srt_time(2)
|
|
|
+ end_time = cls.seconds_to_srt_time(duration)
|
|
|
+ single_video_txt = file_path + 'single_video.txt'
|
|
|
+ with open(single_video_txt, 'w') as f:
|
|
|
+ f.write(f"file '{video_path}'\n")
|
|
|
+ if zm:
|
|
|
+ with open(single_video_srt, 'w') as f:
|
|
|
+ f.write(f"1\n{start_time} --> {end_time}\n<font color=\"red\">\u2764\uFE0F</font>{zm}\n\n")
|
|
|
+ subtitle_cmd = f"subtitles={single_video_srt}:force_style='Fontsize=14,Fontname=wqy-zenhei,Outline=2,PrimaryColour=&H00FFFF,SecondaryColour=&H000000,Bold=1,MarginV=20'"
|
|
|
+ else:
|
|
|
+ subtitle_cmd = f"force_style='Fontsize=14,Fontname=wqy-zenhei,Outline=2,PrimaryColour=&H00FFFF,SecondaryColour=&H000000,Bold=1,MarginV=20'"
|
|
|
+ # 多线程数
|
|
|
+ num_threads = 5
|
|
|
+ # 构建 FFmpeg 命令,生成视频
|
|
|
+ cls.asyncio_run_subprocess([
|
|
|
+ "ffmpeg",
|
|
|
+ "-f", "concat",
|
|
|
+ "-safe", "0",
|
|
|
+ "-i", f"{single_video_txt}",
|
|
|
+ "-c:v", "libx264",
|
|
|
+ "-c:a", "aac",
|
|
|
+ '-b:v', '260k',
|
|
|
+ "-b:a", "96k",
|
|
|
+ "-threads", str(num_threads),
|
|
|
+ "-vf", subtitle_cmd,
|
|
|
+ "-y",
|
|
|
+ single_video_url
|
|
|
+ ], timeout=400)
|
|
|
+ if os.path.exists(single_video_srt):
|
|
|
+ os.remove(single_video_srt)
|
|
|
+ return single_video_url
|
|
|
+ except Exception as e:
|
|
|
return single_video_url
|
|
|
- start_time = cls.seconds_to_srt_time(2)
|
|
|
- end_time = cls.seconds_to_srt_time(duration)
|
|
|
- single_video_txt = file_path + 'single_video.txt'
|
|
|
- with open(single_video_txt, 'w') as f:
|
|
|
- f.write(f"file '{video_path}'\n")
|
|
|
- if zm:
|
|
|
- with open(single_video_srt, 'w') as f:
|
|
|
- f.write(f"1\n{start_time} --> {end_time}\n<font color=\"red\">\u2764\uFE0F</font>{zm}\n\n")
|
|
|
- subtitle_cmd = f"subtitles={single_video_srt}:force_style='Fontsize=14,Fontname=wqy-zenhei,Outline=2,PrimaryColour=&H00FFFF,SecondaryColour=&H000000,Bold=1,MarginV=20'"
|
|
|
- else:
|
|
|
- subtitle_cmd = f"force_style='Fontsize=14,Fontname=wqy-zenhei,Outline=2,PrimaryColour=&H00FFFF,SecondaryColour=&H000000,Bold=1,MarginV=20'"
|
|
|
- # 多线程数
|
|
|
- num_threads = 5
|
|
|
- # 构建 FFmpeg 命令,生成视频
|
|
|
- cls.asyncio_run_subprocess([
|
|
|
- "ffmpeg",
|
|
|
- "-f", "concat",
|
|
|
- "-safe", "0",
|
|
|
- "-i", f"{single_video_txt}",
|
|
|
- "-c:v", "libx264",
|
|
|
- "-c:a", "aac",
|
|
|
- '-b:v', '260k',
|
|
|
- "-b:a", "96k",
|
|
|
- "-threads", str(num_threads),
|
|
|
- "-vf", subtitle_cmd,
|
|
|
- "-y",
|
|
|
- single_video_url
|
|
|
- ], timeout=400)
|
|
|
- if os.path.exists(single_video_srt):
|
|
|
- os.remove(single_video_srt)
|
|
|
- return single_video_url
|
|
|
|
|
|
@classmethod
|
|
|
def asyncio_run_subprocess(cls, params: List[str], timeout: int = 30) -> str:
|