|  | @@ -1,309 +1,376 @@
 | 
	
		
			
				|  |  | -import asyncio
 | 
	
		
			
				|  |  |  import json
 | 
	
		
			
				|  |  | -import os
 | 
	
		
			
				|  |  |  import time
 | 
	
		
			
				|  |  | -from typing import List
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -import cv2
 | 
	
		
			
				|  |  | +import orjson
 | 
	
		
			
				|  |  |  import requests
 | 
	
		
			
				|  |  |  from loguru import logger
 | 
	
		
			
				|  |  | -from mutagen.mp3 import MP3
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +from protos import task_pb2, task_pb2_grpc
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -class FFmpeg():
 | 
	
		
			
				|  |  | +class FFmpeg(object):
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  | +    def __init__(self, container_id: str, stub: task_pb2_grpc.TaskRunnerStub):
 | 
	
		
			
				|  |  | +        self.container_id = container_id
 | 
	
		
			
				|  |  | +        self.stub = stub
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -    时间转换
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -    @classmethod
 | 
	
		
			
				|  |  | -    def seconds_to_srt_time(cls, seconds):
 | 
	
		
			
				|  |  | +    def seconds_to_srt_time(self, seconds):
 | 
	
		
			
				|  |  | +        """
 | 
	
		
			
				|  |  | +        时间转换
 | 
	
		
			
				|  |  | +        """
 | 
	
		
			
				|  |  |          hours = int(seconds // 3600)
 | 
	
		
			
				|  |  |          minutes = int((seconds % 3600) // 60)
 | 
	
		
			
				|  |  |          seconds = seconds % 60
 | 
	
		
			
				|  |  |          milliseconds = int((seconds - int(seconds)) * 1000)
 | 
	
		
			
				|  |  |          return f"{hours:02d}:{minutes:02d}:{int(seconds):02d},{milliseconds:03d}"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -    获取单个视频时长
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -    @classmethod
 | 
	
		
			
				|  |  | -    def get_video_duration(cls, video_url):
 | 
	
		
			
				|  |  | -        cap = cv2.VideoCapture(video_url)
 | 
	
		
			
				|  |  | -        if cap.isOpened():
 | 
	
		
			
				|  |  | -            rate = cap.get(5)
 | 
	
		
			
				|  |  | -            frame_num = cap.get(7)
 | 
	
		
			
				|  |  | -            duration = int(frame_num / rate)
 | 
	
		
			
				|  |  | -            return duration
 | 
	
		
			
				|  |  | -        return 0
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +    # def get_video_duration(self, video_url):
 | 
	
		
			
				|  |  | +    #     """
 | 
	
		
			
				|  |  | +    #     获取单个视频时长
 | 
	
		
			
				|  |  | +    #     """
 | 
	
		
			
				|  |  | +    #     cap = cv2.VideoCapture(video_url)
 | 
	
		
			
				|  |  | +    #     if cap.isOpened():
 | 
	
		
			
				|  |  | +    #         rate = cap.get(5)
 | 
	
		
			
				|  |  | +    #         frame_num = cap.get(7)
 | 
	
		
			
				|  |  | +    #         duration = int(frame_num / rate)
 | 
	
		
			
				|  |  | +    #         return duration
 | 
	
		
			
				|  |  | +    #     return 0
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      # """
 | 
	
		
			
				|  |  |      # 获取视频文件的时长(秒)
 | 
	
		
			
				|  |  |      # """
 | 
	
		
			
				|  |  | -    # @classmethod
 | 
	
		
			
				|  |  | -    # def get_videos_duration(cls, video_file):
 | 
	
		
			
				|  |  | -    #     result = cls.asyncio_run_subprocess(["ffprobe", "-v", "error", "-show_entries", "format=duration",
 | 
	
		
			
				|  |  | +    # def get_videos_duration(self, video_file):
 | 
	
		
			
				|  |  | +    #     result = self.asyncio_run_subprocess(["ffprobe", "-v", "error", "-show_entries", "format=duration",
 | 
	
		
			
				|  |  |      #          "-of", "default=noprint_wrappers=1:nokey=1", video_file], timeout=10)
 | 
	
		
			
				|  |  |      #     return float(result)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -    获取视频宽高
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -    @classmethod
 | 
	
		
			
				|  |  | -    def get_w_h_size(cls, new_video_path):
 | 
	
		
			
				|  |  | +    def get_w_h_size(self, new_video_path):
 | 
	
		
			
				|  |  | +        """
 | 
	
		
			
				|  |  | +        获取视频宽高
 | 
	
		
			
				|  |  | +        """
 | 
	
		
			
				|  |  |          try:
 | 
	
		
			
				|  |  |              # 获取视频的原始宽高信息
 | 
	
		
			
				|  |  | -            ffprobe_cmd = cls.asyncio_run_subprocess(["ffprobe", "-v" ,"error" ,"-select_streams" ,"v:0" ,"-show_entries", "stream=width,height" ,"-of" ,"csv=p=0" ,new_video_path],timeout=10)
 | 
	
		
			
				|  |  | -            output_decoded = ffprobe_cmd.strip()
 | 
	
		
			
				|  |  | +            request = task_pb2.RunCommandRequest(id=self.container_id, command=["ffprobe", "-v" ,"error" ,"-select_streams" ,"v:0" ,"-show_entries", "stream=width,height" ,"-of" ,"csv=p=0" ,new_video_path])
 | 
	
		
			
				|  |  | +            # ffprobe_cmd = self.asyncio_run_subprocess(["ffprobe", "-v" ,"error" ,"-select_streams" ,"v:0" ,"-show_entries", "stream=width,height" ,"-of" ,"csv=p=0" ,new_video_path],timeout=10)
 | 
	
		
			
				|  |  | +            # output_decoded = ffprobe_cmd.strip()
 | 
	
		
			
				|  |  | +            output_decoded = ''
 | 
	
		
			
				|  |  | +            for response in self.stub.RunCommand(request=request):
 | 
	
		
			
				|  |  | +                output_decoded += response.msg
 | 
	
		
			
				|  |  |              split_output = [value for value in output_decoded.split(',') if value.strip()]
 | 
	
		
			
				|  |  |              height, width = map(int, split_output)
 | 
	
		
			
				|  |  |              return width, height
 | 
	
		
			
				|  |  |          except ValueError as e:
 | 
	
		
			
				|  |  |              return 1920, 1080
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -    视频裁剪
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -    @classmethod
 | 
	
		
			
				|  |  | -    def video_crop(cls, video_path, file_path):
 | 
	
		
			
				|  |  | +    def video_crop(self, video_path, file_path):
 | 
	
		
			
				|  |  | +        """
 | 
	
		
			
				|  |  | +        视频裁剪
 | 
	
		
			
				|  |  | +        """
 | 
	
		
			
				|  |  |          crop_url = file_path + 'crop.mp4'
 | 
	
		
			
				|  |  |          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)
 | 
	
		
			
				|  |  | +            # # 获取视频的原始宽高信息
 | 
	
		
			
				|  |  | +            # 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)
 | 
	
		
			
				|  |  | +            request = task_pb2.RunCommandRequest(id=self.container_id, command=["ffmpeg", "-i", video_path, "-vf", f"\"crop=in_w:in_h*0.8\"", "-c:v", "h264_nvenc", "-c:a", "aac", "-y", "-cq", "28", "-preset", "slow", crop_url])
 | 
	
		
			
				|  |  | +            for _ in self.stub.RunCommand(request=request):
 | 
	
		
			
				|  |  | +                pass
 | 
	
		
			
				|  |  | +            # self.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
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -    视频截断
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -    @classmethod
 | 
	
		
			
				|  |  | -    def video_ggduration(cls, video_path, file_path, gg_duration_total):
 | 
	
		
			
				|  |  | +    def video_ggduration(self, video_path, file_path, gg_duration_total):
 | 
	
		
			
				|  |  | +        """
 | 
	
		
			
				|  |  | +        视频截断
 | 
	
		
			
				|  |  | +        """
 | 
	
		
			
				|  |  |          gg_duration_url = file_path + 'gg_duration.mp4'
 | 
	
		
			
				|  |  |          # 获取视频时长
 | 
	
		
			
				|  |  |          try:
 | 
	
		
			
				|  |  | -            total_duration = cls.get_video_duration(video_path)
 | 
	
		
			
				|  |  | +            total_duration = self.get_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)
 | 
	
		
			
				|  |  | +            request = task_pb2.RunCommandRequest(id=self.container_id, command=["ffmpeg", "-i", video_path, "-c:v", "h264_nvenc", "-c:a", "aac", "-t", str(duration), "-y", "-cq", "28", "-preset", "slow", gg_duration_url])
 | 
	
		
			
				|  |  | +            for _ in self.stub.RunCommand(request=request):
 | 
	
		
			
				|  |  | +                pass
 | 
	
		
			
				|  |  | +            # self.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
 | 
	
		
			
				|  |  |          except Exception as e:
 | 
	
		
			
				|  |  |              return gg_duration_url
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -     截取原视频最后一帧
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -    @classmethod
 | 
	
		
			
				|  |  | -    def video_png(cls, video_path, file_path):
 | 
	
		
			
				|  |  | +    def video_png(self, video_path, file_path):
 | 
	
		
			
				|  |  | +        """
 | 
	
		
			
				|  |  | +         截取原视频最后一帧
 | 
	
		
			
				|  |  | +        """
 | 
	
		
			
				|  |  |          # 获取视频的原始宽高信息
 | 
	
		
			
				|  |  |          jpg_url = file_path + 'png.jpg'
 | 
	
		
			
				|  |  |          try:
 | 
	
		
			
				|  |  | -            cls.asyncio_run_subprocess(
 | 
	
		
			
				|  |  | -                ["ffmpeg", "-sseof", "-1", '-i', video_path, '-frames:v', '1',  "-y", jpg_url], timeout=120)
 | 
	
		
			
				|  |  | +            # self.asyncio_run_subprocess(
 | 
	
		
			
				|  |  | +            #     ["ffmpeg", "-sseof", "-1", '-i', video_path, '-frames:v', '1',  "-y", jpg_url], timeout=120)
 | 
	
		
			
				|  |  | +            request = task_pb2.RunCommandRequest(id=self.container_id, command=["ffmpeg", "-sseof", "-1", '-i', video_path, '-frames:v', '1',  "-y", jpg_url])
 | 
	
		
			
				|  |  | +            for _ in self.stub.RunCommand(request=request):
 | 
	
		
			
				|  |  | +                pass
 | 
	
		
			
				|  |  |              return jpg_url
 | 
	
		
			
				|  |  |          except Exception as e:
 | 
	
		
			
				|  |  |              return jpg_url
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -    获取视频音频
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -    @classmethod
 | 
	
		
			
				|  |  | -    def get_video_mp3(cls, video_file, video_path_url, pw_random_id):
 | 
	
		
			
				|  |  | +    def get_video_mp3(self, video_file, video_path_url, pw_random_id):
 | 
	
		
			
				|  |  | +        """
 | 
	
		
			
				|  |  | +        获取视频音频
 | 
	
		
			
				|  |  | +        """
 | 
	
		
			
				|  |  |          pw_mp3_path = video_path_url + str(pw_random_id) +'pw_video.mp3'
 | 
	
		
			
				|  |  |          try:
 | 
	
		
			
				|  |  | -            cls.asyncio_run_subprocess([
 | 
	
		
			
				|  |  | -                'ffmpeg',
 | 
	
		
			
				|  |  | -                '-i', video_file,
 | 
	
		
			
				|  |  | -                '-q:a', '0',
 | 
	
		
			
				|  |  | -                '-map', 'a',
 | 
	
		
			
				|  |  | -                pw_mp3_path
 | 
	
		
			
				|  |  | -            ], timeout=120)
 | 
	
		
			
				|  |  | +            request = task_pb2.RunCommandRequest(id=self.container_id, command=['ffmpeg', '-i', video_file, '-q:a', '0', '-map', 'a', pw_mp3_path])
 | 
	
		
			
				|  |  | +            for _ in self.stub.RunCommand(request=request):
 | 
	
		
			
				|  |  | +                pass
 | 
	
		
			
				|  |  | +            # self.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 get_pw_video_mp3(cls, video_file, video_path_url):
 | 
	
		
			
				|  |  | +    def get_pw_video_mp3(self, video_file, video_path_url):
 | 
	
		
			
				|  |  |          bgm_pw_path_mp3 = video_file + 'bgm_pw.mp3'
 | 
	
		
			
				|  |  |          try:
 | 
	
		
			
				|  |  | -            cls.asyncio_run_subprocess([
 | 
	
		
			
				|  |  | -                'ffmpeg',
 | 
	
		
			
				|  |  | -                '-i', video_path_url,      # 输入的视频文件路径
 | 
	
		
			
				|  |  | -                '-q:a', '0',           # 设置音频质量为最佳
 | 
	
		
			
				|  |  | -                '-map', 'a',
 | 
	
		
			
				|  |  | -                '-y',
 | 
	
		
			
				|  |  | -                bgm_pw_path_mp3
 | 
	
		
			
				|  |  | -            ], timeout=120)
 | 
	
		
			
				|  |  | -            time.sleep(1)
 | 
	
		
			
				|  |  | +            request = task_pb2.RunCommandRequest(id=self.container_id, command=['ffmpeg', '-i', video_path_url, '-q:a', '0', '-map', 'a', bgm_pw_path_mp3])
 | 
	
		
			
				|  |  | +            for _ in self.stub.RunCommand(request=request):
 | 
	
		
			
				|  |  | +                pass
 | 
	
		
			
				|  |  | +            # self.asyncio_run_subprocess([
 | 
	
		
			
				|  |  | +            #     'ffmpeg',
 | 
	
		
			
				|  |  | +            #     '-i', video_path_url,      # 输入的视频文件路径
 | 
	
		
			
				|  |  | +            #     '-q:a', '0',           # 设置音频质量为最佳
 | 
	
		
			
				|  |  | +            #     '-map', 'a',
 | 
	
		
			
				|  |  | +            #     '-y',
 | 
	
		
			
				|  |  | +            #     bgm_pw_path_mp3
 | 
	
		
			
				|  |  | +            # ], timeout=120)
 | 
	
		
			
				|  |  | +            # time.sleep(1)
 | 
	
		
			
				|  |  |              return bgm_pw_path_mp3
 | 
	
		
			
				|  |  |          except Exception as e:
 | 
	
		
			
				|  |  |              return bgm_pw_path_mp3
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -    片尾增加bgm
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -    @classmethod
 | 
	
		
			
				|  |  | -    def video_add_bgm(cls, video_file, bgm_path, video_path_url):
 | 
	
		
			
				|  |  | +    def video_add_bgm(self, video_file, bgm_path, video_path_url):
 | 
	
		
			
				|  |  | +        """
 | 
	
		
			
				|  |  | +        片尾增加bgm
 | 
	
		
			
				|  |  | +        """
 | 
	
		
			
				|  |  |          bgm_pw_path = video_path_url + 'bgm_pw.mp4'
 | 
	
		
			
				|  |  | -        pw_duration = cls.get_video_duration(video_file)
 | 
	
		
			
				|  |  | +        pw_duration = self.get_duration(video_file)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          try:
 | 
	
		
			
				|  |  |              pw_path_txt = video_path_url + 'bgm_pw_video.txt'
 | 
	
		
			
				|  |  | -            with open(pw_path_txt, 'w') as f:
 | 
	
		
			
				|  |  | -                f.write(f"file '{video_file}'\n")
 | 
	
		
			
				|  |  | -            cls.asyncio_run_subprocess([
 | 
	
		
			
				|  |  | -                "ffmpeg",
 | 
	
		
			
				|  |  | -                "-f", "concat",
 | 
	
		
			
				|  |  | -                "-safe", "0",
 | 
	
		
			
				|  |  | -                "-i", f"{pw_path_txt}",  # 视频序列输入的文本文件
 | 
	
		
			
				|  |  | -                "-i", bgm_path,  # 音频文件
 | 
	
		
			
				|  |  | -                "-c:v", "libx264",  # 视频编码格式
 | 
	
		
			
				|  |  | -                "-t", str(pw_duration),  # 输出视频的持续时间
 | 
	
		
			
				|  |  | -                '-c:a', 'aac',           # 音频编码格式
 | 
	
		
			
				|  |  | -                '-b:v', '260k',          # 视频比特率
 | 
	
		
			
				|  |  | -                '-b:a', '96k',           # 音频比特率
 | 
	
		
			
				|  |  | -                '-threads', '2',         # 线程数
 | 
	
		
			
				|  |  | -                # '-vf', f'{background_cmd},{subtitle_cmd}',  # 视频过滤器(背景和字幕)
 | 
	
		
			
				|  |  | -                "-filter_complex", "[1:a]volume=0.6[a1];[0:a][a1]amerge=inputs=2[aout]",  # 混合音频流
 | 
	
		
			
				|  |  | -                "-map", "0:v:0",  # 映射视频流来自第一个输入文件(视频)
 | 
	
		
			
				|  |  | -                "-map", "[aout]",  # 映射混合后的音频流
 | 
	
		
			
				|  |  | -                '-y',                    # 强制覆盖输出文件
 | 
	
		
			
				|  |  | -                bgm_pw_path              # 输出文件路径
 | 
	
		
			
				|  |  | -            ], timeout=500)
 | 
	
		
			
				|  |  | -            time.sleep(1)
 | 
	
		
			
				|  |  | +            # with open(pw_path_txt, 'w') as f:
 | 
	
		
			
				|  |  | +            #     f.write(f"file '{video_file}'\n")
 | 
	
		
			
				|  |  | +            payload = f"file '{video_file}'\n".encode()
 | 
	
		
			
				|  |  | +            request = task_pb2.PutFileRequest(id=self.container_id, payload=payload, path=pw_path_txt)
 | 
	
		
			
				|  |  | +            self.stub.PutFile(request=request)
 | 
	
		
			
				|  |  | +            request = task_pb2.RunCommandRequest(id=self.container_id,
 | 
	
		
			
				|  |  | +                                                 command=[
 | 
	
		
			
				|  |  | +                                                     "ffmpeg",
 | 
	
		
			
				|  |  | +                                                     "-f", "concat",
 | 
	
		
			
				|  |  | +                                                     "-safe", "0",
 | 
	
		
			
				|  |  | +                                                     "-i", f"{pw_path_txt}",  # 视频序列输入的文本文件
 | 
	
		
			
				|  |  | +                                                     "-i", bgm_path,  # 音频文件
 | 
	
		
			
				|  |  | +                                                     "-c:v", "h264_nvenc",  # 视频编码格式
 | 
	
		
			
				|  |  | +                                                     '-c:a', 'aac',           # 音频编码格式
 | 
	
		
			
				|  |  | +                                                     "-t", str(pw_duration),  # 输出视频的持续时间
 | 
	
		
			
				|  |  | +                                                     '-b:v', '260k',          # 视频比特率
 | 
	
		
			
				|  |  | +                                                     '-b:a', '96k',           # 音频比特率
 | 
	
		
			
				|  |  | +                                                     '-threads', '2',         # 线程数
 | 
	
		
			
				|  |  | +                                                     # '-vf', f'{background_cmd},{subtitle_cmd}',  # 视频过滤器(背景和字幕)
 | 
	
		
			
				|  |  | +                                                     "-filter_complex", "\"[1:a]volume=0.6[a1];[0:a][a1]amerge=inputs=2[aout]\"",  # 混合音频流
 | 
	
		
			
				|  |  | +                                                     "-map", "\"0:v:0\"",  # 映射视频流来自第一个输入文件(视频)
 | 
	
		
			
				|  |  | +                                                     "-map", "\"[aout]\"",  # 映射混合后的音频流
 | 
	
		
			
				|  |  | +                                                     '-y',                    # 强制覆盖输出文件
 | 
	
		
			
				|  |  | +                                                     "-cq", "28",
 | 
	
		
			
				|  |  | +                                                     "-preset", "slow",
 | 
	
		
			
				|  |  | +                                                     bgm_pw_path              # 输出文件路径
 | 
	
		
			
				|  |  | +                                                 ])
 | 
	
		
			
				|  |  | +            for _ in self.stub.RunCommand(request=request):
 | 
	
		
			
				|  |  | +                pass
 | 
	
		
			
				|  |  | +            # self.asyncio_run_subprocess([
 | 
	
		
			
				|  |  | +            #     "ffmpeg",
 | 
	
		
			
				|  |  | +            #     "-f", "concat",
 | 
	
		
			
				|  |  | +            #     "-safe", "0",
 | 
	
		
			
				|  |  | +            #     "-i", f"{pw_path_txt}",  # 视频序列输入的文本文件
 | 
	
		
			
				|  |  | +            #     "-i", bgm_path,  # 音频文件
 | 
	
		
			
				|  |  | +            #     "-c:v", "libx264",  # 视频编码格式
 | 
	
		
			
				|  |  | +            #     "-t", str(pw_duration),  # 输出视频的持续时间
 | 
	
		
			
				|  |  | +            #     '-c:a', 'aac',           # 音频编码格式
 | 
	
		
			
				|  |  | +            #     '-b:v', '260k',          # 视频比特率
 | 
	
		
			
				|  |  | +            #     '-b:a', '96k',           # 音频比特率
 | 
	
		
			
				|  |  | +            #     '-threads', '2',         # 线程数
 | 
	
		
			
				|  |  | +            #     # '-vf', f'{background_cmd},{subtitle_cmd}',  # 视频过滤器(背景和字幕)
 | 
	
		
			
				|  |  | +            #     "-filter_complex", "[1:a]volume=0.6[a1];[0:a][a1]amerge=inputs=2[aout]",  # 混合音频流
 | 
	
		
			
				|  |  | +            #     "-map", "0:v:0",  # 映射视频流来自第一个输入文件(视频)
 | 
	
		
			
				|  |  | +            #     "-map", "[aout]",  # 映射混合后的音频流
 | 
	
		
			
				|  |  | +            #     '-y',                    # 强制覆盖输出文件
 | 
	
		
			
				|  |  | +            #     bgm_pw_path              # 输出文件路径
 | 
	
		
			
				|  |  | +            # ], timeout=500)
 | 
	
		
			
				|  |  | +            # time.sleep(1)
 | 
	
		
			
				|  |  |              return bgm_pw_path
 | 
	
		
			
				|  |  |          except Exception as e:
 | 
	
		
			
				|  |  |              return bgm_pw_path
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    """横屏视频改为竖屏"""
 | 
	
		
			
				|  |  | -    @classmethod
 | 
	
		
			
				|  |  | -    def update_video_h_w(cls, video_path, file_path):
 | 
	
		
			
				|  |  | +    def update_video_h_w(self, video_path, file_path):
 | 
	
		
			
				|  |  | +        """横屏视频改为竖屏"""
 | 
	
		
			
				|  |  |          video_h_w_path = file_path +'video_h_w_video.mp4'
 | 
	
		
			
				|  |  |          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)
 | 
	
		
			
				|  |  | +            request = task_pb2.RunCommandRequest(id=self.container_id, command=["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])
 | 
	
		
			
				|  |  | +            for _ in self.stub.RunCommand(request=request):
 | 
	
		
			
				|  |  | +                pass
 | 
	
		
			
				|  |  | +            # self.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):
 | 
	
		
			
				|  |  | +    def video_640(self, video_path, file_path):
 | 
	
		
			
				|  |  | +        """视频转为640像素"""
 | 
	
		
			
				|  |  |          video_url = file_path + 'pixelvideo.mp4'
 | 
	
		
			
				|  |  |          try:
 | 
	
		
			
				|  |  | -            cls.asyncio_run_subprocess(["ffmpeg" ,"-i" ,video_path ,"-vf" ,"scale=360:640" ,video_url],timeout=420)
 | 
	
		
			
				|  |  | +            request = task_pb2.RunCommandRequest(id=self.container_id, command=["ffmpeg" ,"-i" ,video_path ,"-vf" ,"\"scale=360:640\"" ,video_url])
 | 
	
		
			
				|  |  | +            for _ in self.stub.RunCommand(request=request):
 | 
	
		
			
				|  |  | +                pass
 | 
	
		
			
				|  |  | +            # self.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 concatenate_videos(cls, videos_paths, file_path):
 | 
	
		
			
				|  |  | +    def concatenate_videos(self, videos_paths, file_path):
 | 
	
		
			
				|  |  |          video_url = file_path + 'rg_pw.mp4'
 | 
	
		
			
				|  |  |          list_filename = file_path + 'rg_pw.txt'
 | 
	
		
			
				|  |  | -        with open(list_filename, "w") as f:
 | 
	
		
			
				|  |  | -            for video_path in videos_paths:
 | 
	
		
			
				|  |  | -                f.write(f"file '{video_path}'\n")
 | 
	
		
			
				|  |  | +        # with open(list_filename, "w") as f:
 | 
	
		
			
				|  |  | +        #     for video_path in videos_paths:
 | 
	
		
			
				|  |  | +        #         f.write(f"file '{video_path}'\n")
 | 
	
		
			
				|  |  | +        payload = b''
 | 
	
		
			
				|  |  | +        for video_path in videos_paths:
 | 
	
		
			
				|  |  | +            payload += f"file '{video_path}'\n".encode()
 | 
	
		
			
				|  |  | +        request = task_pb2.PutFileRequest(id=self.container_id, payload=payload, path=list_filename)
 | 
	
		
			
				|  |  | +        self.stub.PutFile(request=request)
 | 
	
		
			
				|  |  |          try:
 | 
	
		
			
				|  |  | -            cls.asyncio_run_subprocess(
 | 
	
		
			
				|  |  | -                ["ffmpeg", "-f", "concat", "-safe", "0", "-i", list_filename, "-c", "copy", video_url], timeout=420)
 | 
	
		
			
				|  |  | +            request = task_pb2.RunCommandRequest(id=self.container_id, command=["ffmpeg", "-f", "concat", "-safe", "0", "-i", list_filename, "-c", "copy", video_url])
 | 
	
		
			
				|  |  | +            for _ in self.stub.RunCommand(request=request):
 | 
	
		
			
				|  |  | +                pass
 | 
	
		
			
				|  |  | +            # self.asyncio_run_subprocess(
 | 
	
		
			
				|  |  | +            #     ["ffmpeg", "-f", "concat", "-safe", "0", "-i", list_filename, "-c", "copy", video_url], timeout=420)
 | 
	
		
			
				|  |  |              logger.info(f"[+] 视频转为640像素成功")
 | 
	
		
			
				|  |  |              return video_url
 | 
	
		
			
				|  |  |          except Exception as e:
 | 
	
		
			
				|  |  |              return video_url
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    """视频拼接到一起"""
 | 
	
		
			
				|  |  | -    @classmethod
 | 
	
		
			
				|  |  | -    def h_b_video(cls, video_path, pw_path, file_path):
 | 
	
		
			
				|  |  | +    def h_b_video(self, video_path, pw_path, file_path):
 | 
	
		
			
				|  |  | +        """视频拼接到一起"""
 | 
	
		
			
				|  |  |          video_url = file_path + 'hbvideo.mp4'
 | 
	
		
			
				|  |  |          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)
 | 
	
		
			
				|  |  | +            request = task_pb2.RunCommandRequest(id=self.container_id, command=["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])
 | 
	
		
			
				|  |  | +            for _ in self.stub.RunCommand(request=request):
 | 
	
		
			
				|  |  | +                pass
 | 
	
		
			
				|  |  | +            # self.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
 | 
	
		
			
				|  |  | -    def add_video_zm(cls, new_video_path, video_path_url, pw_random_id, new_text):
 | 
	
		
			
				|  |  | +    def add_video_zm(self, new_video_path, video_path_url, pw_random_id, new_text):
 | 
	
		
			
				|  |  | +        """横屏视频顶部增加字幕"""
 | 
	
		
			
				|  |  |          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'
 | 
	
		
			
				|  |  |          try:
 | 
	
		
			
				|  |  | -            duration = cls.get_video_duration(new_video_path)
 | 
	
		
			
				|  |  | +            duration = self.get_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)
 | 
	
		
			
				|  |  | +            start_time = self.seconds_to_srt_time(0)
 | 
	
		
			
				|  |  | +            end_time = self.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")
 | 
	
		
			
				|  |  | +            payload = f"file '{new_video_path}'\n".encode()
 | 
	
		
			
				|  |  | +            request = task_pb2.PutFileRequest(id=self.container_id, payload=payload, path=single_video_txt)
 | 
	
		
			
				|  |  | +            self.stub.PutFile(request)
 | 
	
		
			
				|  |  | +            # with open(single_video_txt, 'w') as f:
 | 
	
		
			
				|  |  | +            #     f.write(f"file '{new_video_path}'\n")
 | 
	
		
			
				|  |  | +            payload = f"1\n{start_time} --> {end_time}\n{new_text}\n\n".encode()
 | 
	
		
			
				|  |  | +            request = task_pb2.PutFileRequest(id=self.container_id, payload=payload, path=single_video_srt)
 | 
	
		
			
				|  |  | +            self.stub.PutFile(request)
 | 
	
		
			
				|  |  | +            # 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)
 | 
	
		
			
				|  |  | +            request = task_pb2.RunCommandRequest(id=self.container_id,
 | 
	
		
			
				|  |  | +                                                 command=[
 | 
	
		
			
				|  |  | +                                                     "ffmpeg",
 | 
	
		
			
				|  |  | +                                                     "-f", "concat",
 | 
	
		
			
				|  |  | +                                                     "-safe", "0",
 | 
	
		
			
				|  |  | +                                                     "-i", single_video_txt,
 | 
	
		
			
				|  |  | +                                                     "-c:v", "h264_nvenc",
 | 
	
		
			
				|  |  | +                                                     "-c:a", "aac",
 | 
	
		
			
				|  |  | +                                                     "-vf", f"\"{subtitle_cmd}\"",
 | 
	
		
			
				|  |  | +                                                     "-y",
 | 
	
		
			
				|  |  | +                                                     "-cq", "28",
 | 
	
		
			
				|  |  | +                                                     "-preset", "slow",
 | 
	
		
			
				|  |  | +                                                     single_video
 | 
	
		
			
				|  |  | +                                                 ])
 | 
	
		
			
				|  |  | +            for _ in self.stub.RunCommand(request=request):
 | 
	
		
			
				|  |  | +                pass
 | 
	
		
			
				|  |  | +            # self.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
 | 
	
		
			
				|  |  | -    def get_mp3_duration(cls, file_path):
 | 
	
		
			
				|  |  | -        audio = MP3(file_path)
 | 
	
		
			
				|  |  | -        duration = audio.info.length
 | 
	
		
			
				|  |  | -        if duration:
 | 
	
		
			
				|  |  | -            return int(duration)
 | 
	
		
			
				|  |  | -        return 0
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -     生成片尾视频
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -    @classmethod
 | 
	
		
			
				|  |  | -    def pw_video(cls, jpg_path, file_path, pw_mp3_path, pw_srt):
 | 
	
		
			
				|  |  | -        # 添加音频到图片
 | 
	
		
			
				|  |  | +    def get_duration(self, file_path):
 | 
	
		
			
				|  |  | +        """获取mp3时长"""
 | 
	
		
			
				|  |  | +        # audio = MP3(file_path)
 | 
	
		
			
				|  |  | +        # duration = audio.info.length
 | 
	
		
			
				|  |  | +        # if duration:
 | 
	
		
			
				|  |  | +        #     return int(duration)
 | 
	
		
			
				|  |  | +        request = task_pb2.RunCommandRequest(id=self.container_id, command=['ffprobe', '-show_format', '-show_streams', '-of', 'json', '-v', 'quiet', '-hide_banner', file_path])
 | 
	
		
			
				|  |  | +        msg = ''
 | 
	
		
			
				|  |  | +        for response in self.stub.RunCommand(request):
 | 
	
		
			
				|  |  | +            msg += response.msg
 | 
	
		
			
				|  |  | +        obj = orjson.loads(msg)
 | 
	
		
			
				|  |  | +        return int(float(obj['format']['duration']))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def pw_video(self, jpg_path, file_path, pw_mp3_path, pw_srt):
 | 
	
		
			
				|  |  |          """
 | 
	
		
			
				|  |  | +        生成片尾视频
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          jpg_url 图片地址
 | 
	
		
			
				|  |  |          pw_video 提供的片尾视频
 | 
	
		
			
				|  |  |          pw_duration  提供的片尾视频时长
 | 
	
	
		
			
				|  | @@ -312,12 +379,16 @@ class FFmpeg():
 | 
	
		
			
				|  |  |          pw_url 生成视频地址
 | 
	
		
			
				|  |  |          :return:
 | 
	
		
			
				|  |  |          """
 | 
	
		
			
				|  |  | +        # 添加音频到图片
 | 
	
		
			
				|  |  |          pw_srt_path = file_path +'pw_video.srt'
 | 
	
		
			
				|  |  | -        with open(pw_srt_path, 'w') as f:
 | 
	
		
			
				|  |  | -            f.write(pw_srt)
 | 
	
		
			
				|  |  | +        payload = pw_srt.encode()
 | 
	
		
			
				|  |  | +        request = task_pb2.PutFileRequest(id=self.container_id, payload=payload, path=pw_srt_path)
 | 
	
		
			
				|  |  | +        self.stub.PutFile(request=request)
 | 
	
		
			
				|  |  | +        # with open(pw_srt_path, 'w') as f:
 | 
	
		
			
				|  |  | +        #     f.write(pw_srt)
 | 
	
		
			
				|  |  |          pw_url_path = file_path + 'pw_video.mp4'
 | 
	
		
			
				|  |  |          try:
 | 
	
		
			
				|  |  | -            pw_duration = cls.get_mp3_duration(pw_mp3_path)
 | 
	
		
			
				|  |  | +            pw_duration = self.get_duration(pw_mp3_path)
 | 
	
		
			
				|  |  |              if pw_duration == 0:
 | 
	
		
			
				|  |  |                  return pw_url_path
 | 
	
		
			
				|  |  |              time.sleep(2)
 | 
	
	
		
			
				|  | @@ -329,117 +400,182 @@ class FFmpeg():
 | 
	
		
			
				|  |  |              background_cmd = f"drawbox=y=(ih-{int(360)}/2-{bg_position_offset}):color=yellow@1.0:width=iw:height={int(360)}/4:t=fill"
 | 
	
		
			
				|  |  |              if "mp4" in jpg_path:
 | 
	
		
			
				|  |  |                  pw_path_txt = file_path + 'pw_path_video.txt'
 | 
	
		
			
				|  |  | -                with open(pw_path_txt, 'w') as f:
 | 
	
		
			
				|  |  | -                    f.write(f"file '{jpg_path}'\n")
 | 
	
		
			
				|  |  | -                cls.asyncio_run_subprocess([
 | 
	
		
			
				|  |  | -                    "ffmpeg",
 | 
	
		
			
				|  |  | -                    "-f", "concat",
 | 
	
		
			
				|  |  | -                    "-safe", "0",
 | 
	
		
			
				|  |  | -                    "-i", f"{pw_path_txt}",  # 视频序列输入的文本文件
 | 
	
		
			
				|  |  | -                    "-i", pw_mp3_path,  # 音频文件
 | 
	
		
			
				|  |  | -                    "-c:v", "libx264",  # 视频编码格式
 | 
	
		
			
				|  |  | -                    "-t", str(pw_duration),  # 输出视频的持续时间
 | 
	
		
			
				|  |  | -                    "-c:a", "aac",  # 音频编码格式
 | 
	
		
			
				|  |  | -                    "-b:v", "260k",  # 视频比特率
 | 
	
		
			
				|  |  | -                    "-b:a", "96k",  # 音频比特率
 | 
	
		
			
				|  |  | -                    "-threads", "2",  # 线程数
 | 
	
		
			
				|  |  | -                    "-vf", f"{background_cmd},{subtitle_cmd}",  # 视频过滤器(背景和字幕)
 | 
	
		
			
				|  |  | -                    "-map", "0:v:0",  # 映射视频流来自第一个输入文件(视频)
 | 
	
		
			
				|  |  | -                    "-map", "1:a:0",  # 映射音频流来自第二个输入文件(音频)
 | 
	
		
			
				|  |  | -                    "-y",  # 强制覆盖输出文件
 | 
	
		
			
				|  |  | -                    pw_url_path  # 输出文件路径
 | 
	
		
			
				|  |  | -                ], timeout=500)
 | 
	
		
			
				|  |  | +                payload = f"file '{jpg_path}'\n".encode()
 | 
	
		
			
				|  |  | +                request = task_pb2.PutFileRequest(id=self.container_id, payload=payload, path=pw_path_txt)
 | 
	
		
			
				|  |  | +                self.stub.PutFile(request=request)
 | 
	
		
			
				|  |  | +                # with open(pw_path_txt, 'w') as f:
 | 
	
		
			
				|  |  | +                #     f.write(f"file '{jpg_path}'\n")
 | 
	
		
			
				|  |  | +                request = task_pb2.RunCommandRequest(id=self.container_id,
 | 
	
		
			
				|  |  | +                                                     command=[
 | 
	
		
			
				|  |  | +                                                         "ffmpeg",
 | 
	
		
			
				|  |  | +                                                         "-f", "concat",
 | 
	
		
			
				|  |  | +                                                         "-safe", "0",
 | 
	
		
			
				|  |  | +                                                         "-i", f"{pw_path_txt}",  # 视频序列输入的文本文件
 | 
	
		
			
				|  |  | +                                                         "-i", pw_mp3_path,  # 音频文件
 | 
	
		
			
				|  |  | +                                                         "-c:v", "h264_nvenc",  # 视频编码格式
 | 
	
		
			
				|  |  | +                                                         "-t", str(pw_duration),  # 输出视频的持续时间
 | 
	
		
			
				|  |  | +                                                         "-c:a", "aac",  # 音频编码格式
 | 
	
		
			
				|  |  | +                                                         "-b:v", "260k",  # 视频比特率
 | 
	
		
			
				|  |  | +                                                         "-b:a", "96k",  # 音频比特率
 | 
	
		
			
				|  |  | +                                                         "-threads", "2",  # 线程数
 | 
	
		
			
				|  |  | +                                                         "-vf", f"\"{background_cmd},{subtitle_cmd}\"",  # 视频过滤器(背景和字幕)
 | 
	
		
			
				|  |  | +                                                         "-map", "0:v:0",  # 映射视频流来自第一个输入文件(视频)
 | 
	
		
			
				|  |  | +                                                         "-map", "1:a:0",  # 映射音频流来自第二个输入文件(音频)
 | 
	
		
			
				|  |  | +                                                         "-y",  # 强制覆盖输出文件
 | 
	
		
			
				|  |  | +                                                         "-cq", "28",
 | 
	
		
			
				|  |  | +                                                         "-preset", "slow",
 | 
	
		
			
				|  |  | +                                                         pw_url_path  # 输出文件路径
 | 
	
		
			
				|  |  | +                                                     ])
 | 
	
		
			
				|  |  | +                for _ in self.stub.RunCommand(request=request):
 | 
	
		
			
				|  |  | +                    pass
 | 
	
		
			
				|  |  | +                # self.asyncio_run_subprocess([
 | 
	
		
			
				|  |  | +                #     "ffmpeg",
 | 
	
		
			
				|  |  | +                #     "-f", "concat",
 | 
	
		
			
				|  |  | +                #     "-safe", "0",
 | 
	
		
			
				|  |  | +                #     "-i", f"{pw_path_txt}",  # 视频序列输入的文本文件
 | 
	
		
			
				|  |  | +                #     "-i", pw_mp3_path,  # 音频文件
 | 
	
		
			
				|  |  | +                #     "-c:v", "libx264",  # 视频编码格式
 | 
	
		
			
				|  |  | +                #     "-t", str(pw_duration),  # 输出视频的持续时间
 | 
	
		
			
				|  |  | +                #     "-c:a", "aac",  # 音频编码格式
 | 
	
		
			
				|  |  | +                #     "-b:v", "260k",  # 视频比特率
 | 
	
		
			
				|  |  | +                #     "-b:a", "96k",  # 音频比特率
 | 
	
		
			
				|  |  | +                #     "-threads", "2",  # 线程数
 | 
	
		
			
				|  |  | +                #     "-vf", f"{background_cmd},{subtitle_cmd}",  # 视频过滤器(背景和字幕)
 | 
	
		
			
				|  |  | +                #     "-map", "0:v:0",  # 映射视频流来自第一个输入文件(视频)
 | 
	
		
			
				|  |  | +                #     "-map", "1:a:0",  # 映射音频流来自第二个输入文件(音频)
 | 
	
		
			
				|  |  | +                #     "-y",  # 强制覆盖输出文件
 | 
	
		
			
				|  |  | +                #     pw_url_path  # 输出文件路径
 | 
	
		
			
				|  |  | +                # ], timeout=500)
 | 
	
		
			
				|  |  |              else:
 | 
	
		
			
				|  |  | -                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)
 | 
	
		
			
				|  |  | +                request = task_pb2.RunCommandRequest(id=self.container_id,
 | 
	
		
			
				|  |  | +                                                     command=[
 | 
	
		
			
				|  |  | +                                                         'ffmpeg',
 | 
	
		
			
				|  |  | +                                                         '-loop', '1',
 | 
	
		
			
				|  |  | +                                                         '-i', jpg_path,  # 输入的图片文件
 | 
	
		
			
				|  |  | +                                                         '-i', pw_mp3_path,  # 输入的音频文件
 | 
	
		
			
				|  |  | +                                                         '-c:v', 'h264_nvenc',  # 视频编码格式
 | 
	
		
			
				|  |  | +                                                         '-t', str(pw_duration),  # 输出视频的持续时间,与音频持续时间相同
 | 
	
		
			
				|  |  | +                                                         '-pix_fmt', 'yuv420p',  # 像素格式
 | 
	
		
			
				|  |  | +                                                         '-c:a', 'aac',  # 音频编码格式
 | 
	
		
			
				|  |  | +                                                         '-strict', 'experimental',  # 使用实验性编码器
 | 
	
		
			
				|  |  | +                                                         '-shortest',  # 确保输出视频的长度与音频一致
 | 
	
		
			
				|  |  | +                                                         '-vf', f"\"{background_cmd},{subtitle_cmd}\"",  # 视频过滤器,设置分辨率和其他过滤器
 | 
	
		
			
				|  |  | +                                                         "-cq", "28",
 | 
	
		
			
				|  |  | +                                                         "-preset", "slow",
 | 
	
		
			
				|  |  | +                                                         pw_url_path  # 输出的视频文件路径
 | 
	
		
			
				|  |  | +                                                     ])
 | 
	
		
			
				|  |  | +                for _ in self.stub.RunCommand(request=request):
 | 
	
		
			
				|  |  | +                    pass
 | 
	
		
			
				|  |  | +                # self.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
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -    单个视频拼接
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -    @classmethod
 | 
	
		
			
				|  |  | -    def single_video(cls, video_path, file_path, zm):
 | 
	
		
			
				|  |  | +    def single_video(self, video_path, file_path, zm):
 | 
	
		
			
				|  |  | +        """
 | 
	
		
			
				|  |  | +        单个视频拼接
 | 
	
		
			
				|  |  | +        """
 | 
	
		
			
				|  |  |          single_video_url = file_path + 'single_video.mp4'
 | 
	
		
			
				|  |  |          single_video_srt = file_path + 'single_video.srt'
 | 
	
		
			
				|  |  |          # 获取时长
 | 
	
		
			
				|  |  |          try:
 | 
	
		
			
				|  |  | -            duration = cls.get_video_duration(video_path)
 | 
	
		
			
				|  |  | +            duration = self.get_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)
 | 
	
		
			
				|  |  | +            start_time = self.seconds_to_srt_time(2)
 | 
	
		
			
				|  |  | +            end_time = self.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")
 | 
	
		
			
				|  |  | +            # with open(single_video_txt, 'w') as f:
 | 
	
		
			
				|  |  | +            #     f.write(f"file '{video_path}'\n")
 | 
	
		
			
				|  |  | +            payload = f"file '{video_path}'\n".encode()
 | 
	
		
			
				|  |  | +            request = task_pb2.PutFileRequest(id=self.container_id, payload=payload, path=single_video_txt)
 | 
	
		
			
				|  |  | +            self.stub.PutFile(request=request)
 | 
	
		
			
				|  |  |              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")
 | 
	
		
			
				|  |  | +                payload = f"1\n{start_time} --> {end_time}\n<font color=\"red\">\u2764\uFE0F</font>{zm}\n\n".encode()
 | 
	
		
			
				|  |  | +                request = task_pb2.PutFileRequest(id=self.container_id, payload=payload, path=single_video_srt)
 | 
	
		
			
				|  |  | +                self.stub.PutFile(request=request)
 | 
	
		
			
				|  |  | +                # 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)
 | 
	
		
			
				|  |  | +            request = task_pb2.RunCommandRequest(id=self.container_id,
 | 
	
		
			
				|  |  | +                                                 command=[
 | 
	
		
			
				|  |  | +                                                     "ffmpeg",
 | 
	
		
			
				|  |  | +                                                     "-f", "concat",
 | 
	
		
			
				|  |  | +                                                     "-safe", "0",
 | 
	
		
			
				|  |  | +                                                     "-i",  f"{single_video_txt}",
 | 
	
		
			
				|  |  | +                                                     "-c:v", "h264_nvenc",
 | 
	
		
			
				|  |  | +                                                     "-c:a", "aac",
 | 
	
		
			
				|  |  | +                                                     '-b:v', '260k',
 | 
	
		
			
				|  |  | +                                                     "-b:a", "96k",
 | 
	
		
			
				|  |  | +                                                     "-threads", str(num_threads),
 | 
	
		
			
				|  |  | +                                                     "-vf", f"\"{subtitle_cmd}\"",
 | 
	
		
			
				|  |  | +                                                     "-y",
 | 
	
		
			
				|  |  | +                                                     "-cq", "28",
 | 
	
		
			
				|  |  | +                                                     "-preset", "slow",
 | 
	
		
			
				|  |  | +                                                      single_video_url
 | 
	
		
			
				|  |  | +                                                 ])
 | 
	
		
			
				|  |  | +            for _ in self.stub.RunCommand(request=request):
 | 
	
		
			
				|  |  | +                pass
 | 
	
		
			
				|  |  | +            # self.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
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    @classmethod
 | 
	
		
			
				|  |  | -    def asyncio_run_subprocess(cls, params: List[str], timeout: int = 30) -> str:
 | 
	
		
			
				|  |  | -        async def run_subprocess():
 | 
	
		
			
				|  |  | -            process = await asyncio.create_subprocess_exec(
 | 
	
		
			
				|  |  | -                params[0],
 | 
	
		
			
				|  |  | -                *params[1:],
 | 
	
		
			
				|  |  | -                stdout=asyncio.subprocess.PIPE,
 | 
	
		
			
				|  |  | -                stderr=asyncio.subprocess.PIPE,
 | 
	
		
			
				|  |  | -            )
 | 
	
		
			
				|  |  | -            try:
 | 
	
		
			
				|  |  | -                out, err = await asyncio.wait_for(process.communicate(), timeout=timeout)
 | 
	
		
			
				|  |  | -                if process.returncode != 0:
 | 
	
		
			
				|  |  | -                    raise IOError(err)
 | 
	
		
			
				|  |  | -                return out.decode()
 | 
	
		
			
				|  |  | -            except asyncio.TimeoutError:
 | 
	
		
			
				|  |  | -                process.kill()
 | 
	
		
			
				|  |  | -                out, err = await process.communicate()
 | 
	
		
			
				|  |  | -                raise IOError(err)
 | 
	
		
			
				|  |  | -        return asyncio.run(run_subprocess())
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    @classmethod
 | 
	
		
			
				|  |  | -    def get_http_duration(cls, videos_path):
 | 
	
		
			
				|  |  | +    # def asyncio_run_subprocess(self, params: List[str], timeout: int = 30) -> str:
 | 
	
		
			
				|  |  | +    #     async def run_subprocess():
 | 
	
		
			
				|  |  | +    #         process = await asyncio.create_subprocess_exec(
 | 
	
		
			
				|  |  | +    #             params[0],
 | 
	
		
			
				|  |  | +    #             *params[1:],
 | 
	
		
			
				|  |  | +    #             stdout=asyncio.subprocess.PIPE,
 | 
	
		
			
				|  |  | +    #             stderr=asyncio.subprocess.PIPE,
 | 
	
		
			
				|  |  | +    #         )
 | 
	
		
			
				|  |  | +    #         try:
 | 
	
		
			
				|  |  | +    #             out, err = await asyncio.wait_for(process.communicate(), timeout=timeout)
 | 
	
		
			
				|  |  | +    #             if process.returncode != 0:
 | 
	
		
			
				|  |  | +    #                 raise IOError(err)
 | 
	
		
			
				|  |  | +    #             return out.decode()
 | 
	
		
			
				|  |  | +    #         except asyncio.TimeoutError:
 | 
	
		
			
				|  |  | +    #             process.kill()
 | 
	
		
			
				|  |  | +    #             out, err = await process.communicate()
 | 
	
		
			
				|  |  | +    #             raise IOError(err)
 | 
	
		
			
				|  |  | +    #     return asyncio.run(run_subprocess())
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def get_http_duration(self, videos_path):
 | 
	
		
			
				|  |  |          total_duration = 0
 | 
	
		
			
				|  |  |          for video_path in videos_path:
 | 
	
		
			
				|  |  |              url = "http://101.37.24.17:5555/api/v1/ffmpeg/get_meta"
 | 
	
	
		
			
				|  | @@ -546,5 +682,3 @@ if __name__ == '__main__':
 | 
	
		
			
				|  |  |  00:00:32,659 --> 00:00:32,860
 | 
	
		
			
				|  |  |  吧"""
 | 
	
		
			
				|  |  |      FFmpeg.pw_video(jpg_path, file_path, mp3_path, pw_srt)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 |