|
@@ -296,8 +296,11 @@ class AgcVidoe():
|
|
|
@classmethod
|
|
|
def concat_videos_with_subtitles(cls, videos, audio_duration, platform, mark):
|
|
|
# 计算视频文件列表总时长
|
|
|
- total_video_duration = sum(cls.get_video_duration(video_file[3]) for video_file in videos)
|
|
|
- if platform == "koubo" or platform == "zhannei":
|
|
|
+ if platform == "baokuai":
|
|
|
+ total_video_duration = sum(cls.get_video_duration(video_file) for video_file in videos)
|
|
|
+ else:
|
|
|
+ total_video_duration = sum(cls.get_video_duration(video_file[3]) for video_file in videos)
|
|
|
+ if platform == "koubo" or platform == "zhannei" or platform == "baokuai":
|
|
|
# 视频时长大于音频时长
|
|
|
if total_video_duration > audio_duration:
|
|
|
return videos
|
|
@@ -664,4 +667,182 @@ class AgcVidoe():
|
|
|
return ''
|
|
|
except Exception as e:
|
|
|
Common.logger("gs_video").warning(f"{mark}的视频拼接失败:{e}\n")
|
|
|
- return ''
|
|
|
+ return ''
|
|
|
+
|
|
|
+ # 爆款跟随任务
|
|
|
+ @classmethod
|
|
|
+ def video_bk_stitching(cls, ex_list):
|
|
|
+ pq_ids = ex_list["pq_id"]
|
|
|
+ pq_ids_list = pq_ids.split(',') # 账号ID
|
|
|
+ mark_name = ex_list['mark_name'] # 负责人
|
|
|
+ mark = ex_list["mark"] # 标示
|
|
|
+ feishu_id = ex_list["feishu_id"] # 飞书文档ID
|
|
|
+ video_call = ex_list["video_call"] #脚本sheet
|
|
|
+ platform = 'baokuai'
|
|
|
+ list_data = Material.get_allbk_data(feishu_id, video_call, mark)
|
|
|
+ # 如果没有该文件目录则创建,有文件目录的话 则删除文件
|
|
|
+ s_path, v_path, video_path_url, v_oss_path = cls.create_folders(mark)
|
|
|
+ for data in list_data:
|
|
|
+ try:
|
|
|
+ uid = data['uid'] # 音频id
|
|
|
+ srt = data['text'] # srt
|
|
|
+ videos = data['video'].split(',')
|
|
|
+ if srt:
|
|
|
+ # 创建临时字幕文件
|
|
|
+ cls.create_subtitle_file(srt, s_path)
|
|
|
+ Common.logger("bk_video").info(f"S{mark} 文件目录创建成功")
|
|
|
+ # 获取音频
|
|
|
+ audio_video, audio_title = cls.get_audio_url(uid, mark, mark_name)
|
|
|
+ Common.logger("bk_video").info(f"{mark}获取需要拼接的音频成功")
|
|
|
+ # 获取音频秒数
|
|
|
+ audio_duration = cls.get_audio_duration(audio_video)
|
|
|
+ video = random.choice(videos)
|
|
|
+ video_url = cls.get_zn_video(video, mark, mark_name)
|
|
|
+ download_video = Oss.get_bk_url(video_url, video_path_url, video)
|
|
|
+ if download_video:
|
|
|
+ video_files = cls.bk_concatenate_videos(download_video, audio_duration, audio_video, platform, s_path, v_path, mark, v_oss_path)
|
|
|
+ if video_files == "":
|
|
|
+ Common.logger("bk_video").info(f"{mark}的{platform}渠道使用拼接视频为空")
|
|
|
+ continue
|
|
|
+ if os.path.isfile(v_oss_path):
|
|
|
+ Common.logger("bk_video").info(f"{mark}的{platform}渠道新视频生成成功")
|
|
|
+ else:
|
|
|
+ Common.logger("bk_video").info(f"{mark}的{platform}渠道新视频生成失败")
|
|
|
+ continue
|
|
|
+ # 随机生成视频oss_id
|
|
|
+ oss_id = cls.random_id()
|
|
|
+ # 获取新生成视频时长
|
|
|
+ v_path_duration = cls.get_audio_duration(v_oss_path)
|
|
|
+ if v_path_duration > audio_duration + 3 or v_path_duration < audio_duration - 3:
|
|
|
+ print(f"{mark}最终生成视频秒数错误,生成了:{v_path_duration}秒,实际秒数{audio_duration}")
|
|
|
+ Common.logger("gs_video").info(
|
|
|
+ f"{mark}最终生成视频秒数错误,生成了:{v_path_duration}秒,实际秒数{audio_duration}")
|
|
|
+ continue
|
|
|
+ # 上传 oss
|
|
|
+ Common.logger("bk_video").info(f"{mark}上传到 OSS 生成视频id为:{oss_id}")
|
|
|
+ oss_object_key = Oss.stitching_sync_upload_oss(v_oss_path, oss_id)
|
|
|
+ status = oss_object_key.get("status")
|
|
|
+ if status == 200:
|
|
|
+ # 获取 oss 视频地址
|
|
|
+ oss_object_key = oss_object_key.get("oss_object_key")
|
|
|
+ Common.logger("bk_video").info(f"{mark}拼接视频发送成功,OSS 地址:{oss_object_key}")
|
|
|
+ time.sleep(10)
|
|
|
+ Common.logger("bk_video").info(f"{mark}开始视频添加到对应用户")
|
|
|
+ piaoquantv = cls.insert_piaoquantv(oss_object_key, audio_title, pq_ids_list)
|
|
|
+ if piaoquantv:
|
|
|
+ Common.logger("bk_video").info(f"{mark}视频添加到对应用户成功")
|
|
|
+ if os.path.isfile(v_oss_path):
|
|
|
+ os.remove(v_oss_path)
|
|
|
+ if os.path.isfile(v_path):
|
|
|
+ os.remove(v_path)
|
|
|
+ if os.path.isfile(s_path):
|
|
|
+ os.remove(s_path)
|
|
|
+ # 清空所有mp4数据
|
|
|
+ for file_path in download_video:
|
|
|
+ os.remove(file_path)
|
|
|
+ print(f"已删除文件:{file_path}")
|
|
|
+ else:
|
|
|
+ Common.logger("bk_video").info(f"{mark}的视频下载视频")
|
|
|
+ continue
|
|
|
+ except Exception as e:
|
|
|
+ Common.logger("bk_video").warning(f"{mark}的视频拼接失败:{e}\n")
|
|
|
+ continue
|
|
|
+ Feishu.bot('recommend', 'AGC完成通知', f'今日脚本爆款视频拼接任务完成,共{len(list_data)}条', mark.split("-")[0], mark_name)
|
|
|
+ return mark
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def get_zn_video(cls, video, mark, mark_name):
|
|
|
+ cookie = Material.get_houtai_cookie()
|
|
|
+ url = f"https://admin.piaoquantv.com/manager/video/detail/{video}"
|
|
|
+ payload = {}
|
|
|
+ headers = {
|
|
|
+ 'authority': 'admin.piaoquantv.com',
|
|
|
+ 'accept': 'application/json, text/plain, */*',
|
|
|
+ 'accept-language': 'zh-CN,zh;q=0.9',
|
|
|
+ 'cache-control': 'no-cache',
|
|
|
+ 'cookie': cookie,
|
|
|
+ 'pragma': 'no-cache',
|
|
|
+ 'referer': f'https://admin.piaoquantv.com/cms/post-detail/{video}/detail',
|
|
|
+ 'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
|
|
|
+ 'sec-ch-ua-mobile': '?0',
|
|
|
+ 'sec-ch-ua-platform': '"macOS"',
|
|
|
+ 'sec-fetch-dest': 'empty',
|
|
|
+ 'sec-fetch-mode': 'cors',
|
|
|
+ 'sec-fetch-site': 'same-origin',
|
|
|
+ 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
|
|
|
+ }
|
|
|
+
|
|
|
+ response = requests.request("GET", url, headers=headers, data=payload)
|
|
|
+ data = response.json()
|
|
|
+ code = data["code"]
|
|
|
+ if code != 0:
|
|
|
+ if "-" in mark:
|
|
|
+ mark1 = mark.split("-")[0]
|
|
|
+ Common.logger("video").info(
|
|
|
+ f"未登录,请更换cookie,{data}")
|
|
|
+ Feishu.bot('recommend', '管理后台', '管理后台cookie失效,请及时更换~', mark1, mark_name)
|
|
|
+ return
|
|
|
+ video_url = data["content"]["transedVideoPath"]
|
|
|
+ return video_url
|
|
|
+
|
|
|
+ # text文件没有则创建目录
|
|
|
+ @classmethod
|
|
|
+ def bk_text_folders(cls, mark):
|
|
|
+ oss_id = cls.random_id()
|
|
|
+ v_text_url = config['PATHS']['VIDEO_PATH'] + mark + "/text/"
|
|
|
+ if not os.path.exists(v_text_url):
|
|
|
+ os.makedirs(v_text_url)
|
|
|
+ # srt 文件地址
|
|
|
+ text_path = v_text_url + mark + f"{str(oss_id)}.text"
|
|
|
+ return text_path
|
|
|
+
|
|
|
+ # 爆款视频拼接
|
|
|
+ @classmethod
|
|
|
+ def bk_concatenate_videos(cls, videos, audio_duration, audio_video, platform, s_path, v_path, mark, v_oss_path):
|
|
|
+ text_ptah = cls.bk_text_folders(mark)
|
|
|
+ video_files = cls.concat_videos_with_subtitles(videos, audio_duration, platform, mark)
|
|
|
+ with open(text_ptah, 'w') as f:
|
|
|
+ for file in video_files:
|
|
|
+ f.write(f"file '{file}'\n")
|
|
|
+ Common.logger("video").info(f"{mark}的{platform}视频文件:{video_files}")
|
|
|
+ if video_files == "":
|
|
|
+ return ""
|
|
|
+ print(f"{mark}的{platform}:开始拼接视频喽~~~")
|
|
|
+ Common.logger("video").info(f"{mark}的{platform}:开始拼接视频喽~~~")
|
|
|
+ if os.path.exists(s_path):
|
|
|
+ # subtitle_cmd = f"subtitles={s_path}:force_style='Fontsize=11,Fontname=Hiragino Sans GB,Outline=0,PrimaryColour=&H000000,SecondaryColour=&H000000'"
|
|
|
+ subtitle_cmd = f"subtitles={s_path}:force_style='Fontsize=12,Fontname=wqy-zenhei,Bold=1,Outline=0,PrimaryColour=&H000000,SecondaryColour=&H000000'"
|
|
|
+ else:
|
|
|
+ # subtitle_cmd = "drawtext=text='分享、转发给群友':fontsize=28:fontcolor=black:x=(w-text_w)/2:y=h-text_h-15"
|
|
|
+ subtitle_cmd = "drawtext=text='分享、转发给群友':x=(w-text_w)/2:y=h-text_h-15:fontsize=28:fontcolor=black:fontfile=/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc"
|
|
|
+ # 背景色参数
|
|
|
+ background_cmd = "drawbox=y=ih-65:color=yellow@1.0:width=iw:height=0:t=fill"
|
|
|
+ # 多线程数
|
|
|
+ num_threads = 4
|
|
|
+ # 构建 FFmpeg 命令,生成视频
|
|
|
+ ffmpeg_cmd_oss = [
|
|
|
+ "ffmpeg",
|
|
|
+ "-f", "concat",
|
|
|
+ "-safe", "0",
|
|
|
+ "-i", f"{text_ptah}", # 视频文件列表
|
|
|
+ "-i", audio_video, # 音频文件
|
|
|
+ "-c:v", "libx264",
|
|
|
+ "-c:a", "aac",
|
|
|
+ "-threads", str(num_threads),
|
|
|
+ "-vf", f"scale=320x480,{background_cmd},{subtitle_cmd}", # 添加背景色和字幕
|
|
|
+ "-t", str(int(audio_duration)), # 保持与音频时长一致
|
|
|
+ "-map", "0:v:0", # 映射第一个输入的视频流
|
|
|
+ "-map", "1:a:0", # 映射第二个输入的音频流
|
|
|
+ "-y", # 覆盖输出文件
|
|
|
+ v_oss_path
|
|
|
+ ]
|
|
|
+ try:
|
|
|
+ subprocess.run(ffmpeg_cmd_oss)
|
|
|
+ print("视频处理完成!")
|
|
|
+ if os.path.isfile(text_ptah):
|
|
|
+ os.remove(text_ptah)
|
|
|
+ except subprocess.CalledProcessError as e:
|
|
|
+ print(f"视频处理失败:{e}")
|
|
|
+ print(f"{mark}:视频拼接成功啦~~~")
|
|
|
+ Common.logger("video").info(f"{mark}:视频拼接成功啦~~~")
|
|
|
+ return video_files
|