agc_video.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. import configparser
  2. import os
  3. import random
  4. import subprocess
  5. import sys
  6. import time
  7. import shutil
  8. from datetime import datetime
  9. from common.sql_help import sqlHelp
  10. sys.path.append(os.getcwd())
  11. from common.material import Material
  12. from common import Common, Oss, Feishu, PQ
  13. from common.srt import SRT
  14. config = configparser.ConfigParser()
  15. config.read('./config.ini') # 替换为您的配置文件路径
  16. class AGC():
  17. """清除文件下所有mp4文件"""
  18. @classmethod
  19. def clear_mp4_files(cls, mark):
  20. video_path_url = config['PATHS']['VIDEO_PATH'] + mark + "/"
  21. # 获取文件夹中所有扩展名为 '.mp4' 的文件路径列表
  22. if os.path.exists(video_path_url):
  23. # 列出目录中的所有文件和文件夹
  24. for filename in os.listdir(video_path_url):
  25. file_path = os.path.join(video_path_url, filename)
  26. try:
  27. # 如果是文件,则删除
  28. if os.path.isfile(file_path) or os.path.islink(file_path):
  29. os.unlink(file_path)
  30. # 如果是文件夹,则删除文件夹及其内容
  31. elif os.path.isdir(file_path):
  32. shutil.rmtree(file_path)
  33. except Exception as e:
  34. print(f"Failed to delete {file_path}. Reason: {e}")
  35. print(f"文件已清空。")
  36. else:
  37. print(f"文件已清空。")
  38. """
  39. 站外视频拼接
  40. """
  41. @classmethod
  42. def zw_concatenate_videos(cls, videos, audio_duration, audio_video, platform, s_path, v_path, mark, v_oss_path):
  43. video_files = cls.concat_videos_with_subtitles(videos, audio_duration, platform, mark)
  44. Common.logger("video").info(f"{mark}的{platform}视频文件:{video_files}")
  45. if video_files == "":
  46. return ""
  47. print(f"{mark}的{platform}:开始拼接视频喽~~~")
  48. Common.logger("video").info(f"{mark}的{platform}:开始拼接视频喽~~~")
  49. if os.path.exists(s_path):
  50. # subtitle_cmd = f"subtitles={s_path}:force_style='Fontsize=11,Fontname=Hiragino Sans GB,Outline=0,PrimaryColour=&H000000,SecondaryColour=&H000000'"
  51. subtitle_cmd = f"subtitles={s_path}:force_style='Fontsize=12,Fontname=wqy-zenhei,Bold=1,Outline=0,PrimaryColour=&H000000,SecondaryColour=&H000000'"
  52. else:
  53. start_time = cls.seconds_to_srt_time(0)
  54. end_time = cls.seconds_to_srt_time(audio_duration)
  55. with open(s_path, 'w') as f:
  56. f.write(f"1\n{start_time} --> {end_time}\n分享、转发给群友\n")
  57. # subtitle_cmd = "drawtext=text='分享、转发给群友':fontsize=28:fontcolor=black:x=(w-text_w)/2:y=h-text_h-15"
  58. subtitle_cmd = f"subtitles={s_path}:force_style='Fontsize=12,Fontname=wqy-zenhei,Bold=1,Outline=0,PrimaryColour=&H000000,SecondaryColour=&H000000'"
  59. # 背景色参数
  60. background_cmd = "drawbox=y=ih-65:color=yellow@1.0:width=iw:height=0:t=fill"
  61. VIDEO_COUNTER = 0
  62. FF_INPUT = ""
  63. FF_SCALE = ""
  64. FF_FILTER = ""
  65. ffmpeg_cmd = ["ffmpeg"]
  66. for videos in video_files:
  67. Common.logger("video").info(f"{mark}的{platform}视频:{videos[3]}")
  68. # 添加输入文件
  69. FF_INPUT += f" -i {videos[3]}"
  70. # 为每个视频文件统一长宽,并设置SAR(采样宽高比)
  71. FF_SCALE += f"[{VIDEO_COUNTER}:v]scale=320x480,setsar=1[v{VIDEO_COUNTER}];"
  72. # 为每个视频文件创建一个输入流,并添加到-filter_complex参数中
  73. FF_FILTER += f"[v{VIDEO_COUNTER}][{VIDEO_COUNTER}:a]"
  74. # 增加视频计数器
  75. VIDEO_COUNTER += 1
  76. # 构建最终的FFmpeg命令
  77. ffmpeg_cmd.extend(FF_INPUT.split())
  78. ffmpeg_cmd.extend(["-filter_complex", f"{FF_SCALE}{FF_FILTER}concat=n={VIDEO_COUNTER}:v=1:a=1[v][a]",
  79. "-map", "[v]", "-map", "[a]", v_path])
  80. # 多线程数
  81. num_threads = 4
  82. # 构建 FFmpeg 命令,生成视频
  83. ffmpeg_cmd_oss = [
  84. "ffmpeg",
  85. "-i", v_path, # 视频文件列表
  86. "-i", audio_video, # 音频文件
  87. "-c:v", "libx264", # 复制视频流
  88. "-c:a", "aac", # 编码音频流为AAC
  89. "-threads", str(num_threads),
  90. "-vf", f"{background_cmd},{subtitle_cmd}", # 添加背景色和字幕
  91. "-t", str(int(audio_duration)), # 保持与音频时长一致
  92. "-map", "0:v:0", # 映射第一个输入的视频流
  93. "-map", "1:a:0", # 映射第二个输入的音频流
  94. "-y", # 覆盖输出文件
  95. v_oss_path
  96. ]
  97. try:
  98. timeout_seconds = 25 * 60
  99. subprocess.run(ffmpeg_cmd, timeout=timeout_seconds)
  100. if os.path.isfile(v_path):
  101. subprocess.run(ffmpeg_cmd_oss, timeout=timeout_seconds)
  102. print("视频处理完成!")
  103. except subprocess.TimeoutExpired:
  104. print("视频处理超时,处理失败!")
  105. return ""
  106. except subprocess.CalledProcessError as e:
  107. print(f"视频处理失败:{e}")
  108. return ""
  109. print(f"{mark}的{platform}:视频拼接成功啦~~~")
  110. Common.logger("video").info(f"{mark}的{platform}:视频拼接成功啦~~~")
  111. return video_files
  112. """视频秒数转换"""
  113. @classmethod
  114. def seconds_to_srt_time(cls, seconds):
  115. hours = int(seconds // 3600)
  116. minutes = int((seconds % 3600) // 60)
  117. seconds = seconds % 60
  118. milliseconds = int((seconds - int(seconds)) * 1000)
  119. return f"{hours:02d}:{minutes:02d}:{int(seconds):02d},{milliseconds:03d}"
  120. """
  121. 获取视频文件的时长(秒)
  122. """
  123. @classmethod
  124. def get_video_duration(cls, video_file):
  125. result = subprocess.run(
  126. ["ffprobe", "-v", "error", "-show_entries", "format=duration",
  127. "-of", "default=noprint_wrappers=1:nokey=1", video_file],
  128. capture_output=True, text=True)
  129. return float(result.stdout)
  130. """计算需要拼接的视频"""
  131. @classmethod
  132. def concat_videos_with_subtitles(cls, videos, audio_duration, platform, mark):
  133. # 计算视频文件列表总时长
  134. if platform == "爆款" or platform == "跟随":
  135. total_video_duration = sum(cls.get_video_duration(video_file) for video_file in videos)
  136. else:
  137. total_video_duration = sum(cls.get_video_duration(video_file[3]) for video_file in videos)
  138. if platform == "爆款" or platform == "跟随":
  139. # 视频时长大于音频时长
  140. if total_video_duration > audio_duration:
  141. return videos
  142. # 计算音频秒数与视频秒数的比率,然后加一得到需要的视频数量
  143. video_audio_ratio = audio_duration / total_video_duration
  144. videos_needed = int(video_audio_ratio) + 2
  145. trimmed_video_list = videos * videos_needed
  146. return trimmed_video_list
  147. else:
  148. # 如果视频总时长小于音频时长,则不做拼接
  149. if total_video_duration < audio_duration:
  150. Common.logger("video").info(f"{mark}的{platform}渠道时长小于等于目标时长,不做视频拼接")
  151. return ""
  152. # 如果视频总时长大于音频时长,则截断视频
  153. trimmed_video_list = []
  154. remaining_duration = audio_duration
  155. for video_file in videos:
  156. video_duration = cls.get_video_duration(video_file[3])
  157. if video_duration <= remaining_duration:
  158. # 如果视频时长小于或等于剩余时长,则将整个视频添加到列表中
  159. trimmed_video_list.append(video_file)
  160. remaining_duration -= video_duration
  161. else:
  162. trimmed_video_list.append(video_file)
  163. break
  164. return trimmed_video_list
  165. """
  166. text文件没有则创建目录
  167. """
  168. @classmethod
  169. def bk_text_folders(cls, mark):
  170. oss_id = cls.random_id()
  171. v_text_url = config['PATHS']['VIDEO_PATH'] + mark + "/text/"
  172. if not os.path.exists(v_text_url):
  173. os.makedirs(v_text_url)
  174. # srt 文件地址
  175. text_path = v_text_url + mark + f"{str(oss_id)}.text"
  176. return text_path
  177. """
  178. 站内视频拼接
  179. """
  180. @classmethod
  181. def zn_concatenate_videos(cls, videos, audio_duration, audio_video, platform, s_path, mark, v_oss_path):
  182. text_ptah = cls.bk_text_folders(mark)
  183. video_files = cls.concat_videos_with_subtitles(videos, audio_duration, platform, mark)
  184. with open(text_ptah, 'w') as f:
  185. for file in video_files:
  186. f.write(f"file '{file}'\n")
  187. Common.logger("video").info(f"{mark}的{platform}视频文件:{video_files}")
  188. if video_files == "":
  189. return ""
  190. print(f"{mark}的{platform}:开始拼接视频喽~~~")
  191. Common.logger("video").info(f"{mark}的{platform}:开始拼接视频喽~~~")
  192. if os.path.exists(s_path):
  193. # subtitle_cmd = f"subtitles={s_path}:force_style='Fontsize=11,Fontname=Hiragino Sans GB,Outline=0,PrimaryColour=&H000000,SecondaryColour=&H000000'"
  194. subtitle_cmd = f"subtitles={s_path}:force_style='Fontsize=12,Fontname=wqy-zenhei,Bold=1,Outline=0,PrimaryColour=&H000000,SecondaryColour=&H000000'"
  195. else:
  196. start_time = cls.seconds_to_srt_time(0)
  197. end_time = cls.seconds_to_srt_time(audio_duration)
  198. with open(s_path, 'w') as f:
  199. f.write(f"1\n{start_time} --> {end_time}\n分享、转发给群友\n")
  200. # subtitle_cmd = "drawtext=text='分享、转发给群友':fontsize=28:fontcolor=black:x=(w-text_w)/2:y=h-text_h-15"
  201. subtitle_cmd = f"subtitles={s_path}:force_style='Fontsize=12,Fontname=wqy-zenhei,Bold=1,Outline=0,PrimaryColour=&H000000,SecondaryColour=&H000000'"
  202. # 背景色参数
  203. background_cmd = "drawbox=y=ih-65:color=yellow@1.0:width=iw:height=0:t=fill"
  204. # 多线程数
  205. num_threads = 4
  206. # 构建 FFmpeg 命令,生成视频
  207. ffmpeg_cmd_oss = [
  208. "ffmpeg",
  209. "-f", "concat",
  210. "-safe", "0",
  211. "-i", f"{text_ptah}", # 视频文件列表
  212. "-i", audio_video, # 音频文件
  213. "-c:v", "libx264",
  214. "-c:a", "aac",
  215. "-threads", str(num_threads),
  216. "-vf", f"scale=320x480,{background_cmd},{subtitle_cmd}", # 添加背景色和字幕
  217. "-t", str(int(audio_duration)), # 保持与音频时长一致
  218. "-map", "0:v:0", # 映射第一个输入的视频流
  219. "-map", "1:a:0", # 映射第二个输入的音频流
  220. "-y", # 覆盖输出文件
  221. v_oss_path
  222. ]
  223. try:
  224. subprocess.run(ffmpeg_cmd_oss)
  225. print("视频处理完成!")
  226. if os.path.isfile(text_ptah):
  227. os.remove(text_ptah)
  228. except subprocess.CalledProcessError as e:
  229. print(f"视频处理失败:{e}")
  230. print(f"{mark}:视频拼接成功啦~~~")
  231. Common.logger("video").info(f"{mark}:视频拼接成功啦~~~")
  232. return v_oss_path
  233. """
  234. 获取视频时长
  235. """
  236. @classmethod
  237. def get_audio_duration(cls, video_url):
  238. ffprobe_cmd = [
  239. "ffprobe",
  240. "-i", video_url,
  241. "-show_entries", "format=duration",
  242. "-v", "quiet",
  243. "-of", "csv=p=0"
  244. ]
  245. output = subprocess.check_output(ffprobe_cmd).decode("utf-8").strip()
  246. return float(output)
  247. """
  248. 创建临时字幕
  249. """
  250. @classmethod
  251. def create_subtitle_file(cls, srt, s_path):
  252. with open(s_path, 'w') as f:
  253. f.write(srt)
  254. """
  255. 随机生成id
  256. """
  257. @classmethod
  258. def random_id(cls):
  259. now = datetime.now()
  260. rand_num = random.randint(10000, 99999)
  261. oss_id = "{}{}".format(now.strftime("%Y%m%d%H%M%S"), rand_num)
  262. return oss_id
  263. """
  264. 文件没有则创建目录
  265. """
  266. @classmethod
  267. def create_folders(cls, mark):
  268. oss_id = cls.random_id()
  269. video_path_url = config['PATHS']['VIDEO_PATH'] + mark + "/"
  270. # srt 目录
  271. s_path_url = config['PATHS']['VIDEO_PATH'] + mark + "/srt/"
  272. # oss 目录
  273. v_path_url = config['PATHS']['VIDEO_PATH'] + mark + "/oss/"
  274. if not os.path.exists(video_path_url):
  275. os.makedirs(video_path_url)
  276. if not os.path.exists(s_path_url):
  277. os.makedirs(s_path_url)
  278. if not os.path.exists(v_path_url):
  279. os.makedirs(v_path_url)
  280. # srt 文件地址
  281. s_path = s_path_url + mark + f"{str(oss_id)}.srt"
  282. # 最终生成视频地址
  283. v_path = v_path_url + mark + f"{str(oss_id)}.mp4"
  284. v_oss_path = v_path_url + mark + f"{str(oss_id)}oss.mp4"
  285. return s_path, v_path, video_path_url, v_oss_path
  286. """
  287. 获取未使用的数据
  288. """
  289. @classmethod
  290. def get_unique_uid_data(cls, data, count):
  291. unique_data_dict = {item['uid']: item for item in data}
  292. unique_data = list(unique_data_dict.values())
  293. if count >= len(unique_data):
  294. return unique_data
  295. else:
  296. selected_items = []
  297. selected_uids = set()
  298. while len(selected_items) < count:
  299. # 随机选择一个元素
  300. item = random.choice(unique_data)
  301. uid = item['uid']
  302. if uid not in selected_uids:
  303. # 如果该uid还未被选择过,则将该元素添加到选中项列表中,并记录已选择的uid
  304. selected_items.append(item)
  305. selected_uids.add(uid)
  306. return selected_items
  307. """
  308. 任务处理
  309. """
  310. @classmethod
  311. def video(cls, data, platform):
  312. mark_name = data['mark_name'] # 负责人
  313. if platform == "爆款":
  314. pq_ids = data["pq_id"]
  315. pq_ids_list = pq_ids.split(',') # 账号ID
  316. mark = data["mark"] # 标示
  317. feishu_id = data["feishu_id"] # 飞书文档ID
  318. video_call = data["video_call"] # 脚本sheet
  319. list_data = Material.get_allbk_data(feishu_id, video_call)
  320. if len(list_data) == 0:
  321. Feishu.bot('recommend', 'AGC完成通知', f'爆款任务数为0,不做拼接', mark, mark_name)
  322. return mark
  323. elif platform == "常规":
  324. pq_ids = data["pq_id"]# 账号ID
  325. pq_ids_list = pq_ids.split(',')
  326. mark = data["mark"]
  327. feishu_id = data["feishu_id"] # 飞书文档ID
  328. video_call = data["video_call"] # 脚本sheet
  329. video_count = data["video_count"]
  330. if int(video_count) == 0:
  331. Feishu.bot('recommend', 'AGC完成通知', f'常规任务数为{video_count},不做拼接', mark, mark_name)
  332. return mark
  333. data_list, videos_mark = Material.get_all_data(feishu_id, video_call, mark)
  334. list_data = cls.get_unique_uid_data(data_list, int(video_count))
  335. elif platform == "跟随":
  336. pq_ids = data["pq_id"]
  337. pq_ids_list = pq_ids.split(',') # 账号ID
  338. mark = data["mark"] # 标示
  339. feishu_id = data["feishu_id"] # 飞书文档ID
  340. video_call = data["video_call"]
  341. video_count = data["video_count"]
  342. if int(video_count) == 0:
  343. Feishu.bot('recommend', 'AGC完成通知', f'跟随任务数为{video_count},不做拼接', mark, mark_name)
  344. return mark
  345. data_list, videos = Material.get_all_data(feishu_id, video_call, mark)
  346. list_data = cls.get_unique_uid_data(data_list, int(video_count))
  347. count = 0
  348. while True:
  349. # 清空所有文件
  350. cls.clear_mp4_files(mark)
  351. s_path, v_path, video_path_url, v_oss_path = cls.create_folders(mark)
  352. if count == len(list_data):
  353. break
  354. # for d_list in list_data:
  355. try:
  356. d_list = list_data[count]
  357. uid = d_list['uid'] # 音频id
  358. srt = d_list['text'] # srt
  359. cover = d_list['cover']
  360. audio_title = d_list['title']
  361. # if srt and len(srt.strip()) >= 3:
  362. # # 创建临时字幕文件
  363. # cls.create_subtitle_file(srt, s_path)
  364. # Common.logger("bk_video").info(f"S{mark} 文件目录创建成功")
  365. # else:
  366. srt_new = SRT.getSrt(int(uid))
  367. Common.logger("video").info(f"S{mark}的{platform}渠道音频ID")
  368. if srt_new:
  369. current_time = datetime.now()
  370. formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S")
  371. values = [[mark, str(uid), srt_new, formatted_time]]
  372. random_wait_time = random.uniform(0.5, 2.5)
  373. time.sleep(random_wait_time)
  374. Feishu.insert_columns("IbVVsKCpbhxhSJtwYOUc8S1jnWb", "jd9qD9", "ROWS", 1, 2)
  375. time.sleep(random_wait_time)
  376. Feishu.update_values("IbVVsKCpbhxhSJtwYOUc8S1jnWb", "jd9qD9", "A2:Z2", values)
  377. # 创建临时字幕文件
  378. cls.create_subtitle_file(srt_new, s_path)
  379. Common.logger("video").info(f"S{mark}的{platform}渠道SRT 文件目录创建成功")
  380. # 获取音频
  381. audio_video = PQ.get_audio_url(uid)
  382. Common.logger("video").info(f"{mark}的{platform}渠道获音频成功")
  383. audio_duration = cls.get_audio_duration(audio_video)
  384. Common.logger("video").info(f"{mark}的{platform}渠道获取需要拼接的音频秒数为:{audio_duration}")
  385. if platform != "常规":
  386. if platform == "爆款":
  387. videos = str(d_list['video'])
  388. if ',' in videos:
  389. videos = str(videos).split(',')
  390. else:
  391. videos = [str(videos)]
  392. video_id = random.choice(videos)
  393. video_url = PQ.get_audio_url(video_id)
  394. download_video = Oss.download_url(video_url, video_path_url, str(video_id))
  395. if download_video:
  396. video_files = cls.zn_concatenate_videos(download_video, audio_duration, audio_video, platform,
  397. s_path, mark, v_oss_path)
  398. if os.path.isfile(v_oss_path):
  399. Common.logger("video").info(f"{mark}的{platform}渠道新视频生成成功")
  400. else:
  401. Common.logger("video").info(f"{mark}的{platform}渠道新视频生成失败")
  402. continue
  403. else:
  404. # chnnel_count = int(len(list_data)/2)
  405. channels = ["douyin", "kuaishou"]
  406. channel = random.choice(channels)
  407. user_id = sqlHelp.get_user_id(channel, mark)
  408. url_list, user = sqlHelp.get_url_list(user_id, mark, "35")
  409. videos = [list(item) for item in url_list]
  410. videos = Oss.get_oss_url(videos, video_path_url)
  411. video_files = cls.zw_concatenate_videos(videos, audio_duration, audio_video, platform, s_path, v_path,
  412. mark, v_oss_path)
  413. if video_files == "":
  414. Common.logger("video").info(f"{mark}的{platform}渠道使用拼接视频为空")
  415. continue
  416. if os.path.isfile(v_oss_path):
  417. Common.logger("video").info(f"{mark}的{platform}渠道新视频生成成功")
  418. else:
  419. Common.logger("video").info(f"{mark}的{platform}渠道新视频生成失败")
  420. continue
  421. # 随机生成视频oss_id
  422. oss_id = cls.random_id()
  423. Common.logger("video").info(f"{mark}的{platform}渠道上传到 OSS 生成视频id为:{oss_id}")
  424. oss_object_key = Oss.stitching_sync_upload_oss(v_oss_path, oss_id)
  425. status = oss_object_key.get("status")
  426. if status == 200:
  427. # 获取 oss 视频地址
  428. oss_object_key = oss_object_key.get("oss_object_key")
  429. Common.logger("video").info(f"{mark}的{platform}渠道拼接视频发送成功,OSS 地址:{oss_object_key}")
  430. time.sleep(10)
  431. if platform == "常规":
  432. # 已使用视频存入数据库
  433. Common.logger("video").info(f"{mark}的{platform}渠道开始已使用视频存入数据库")
  434. sqlHelp.insert_videoAudio(video_files, uid, platform, mark)
  435. Common.logger("video").info(f"{mark}的{platform}渠道完成已使用视频存入数据库")
  436. Common.logger("video").info(f"{mark}的{platform}渠道开始视频添加到对应用户")
  437. new_video_id, title = PQ.insert_piaoquantv(oss_object_key, audio_title, pq_ids_list, cover, uid)
  438. if new_video_id:
  439. Common.logger("video").info(f"{mark}的{platform}渠道视频添加到对应用户成功")
  440. count += 1
  441. if mark_name == "穆新艺":
  442. sheet = '50b8a1'
  443. elif mark_name == "信欣":
  444. sheet = 'UyVK7y'
  445. elif mark_name == "范军":
  446. sheet = 'uP3zbf'
  447. elif mark_name == "鲁涛":
  448. sheet = 'iDTHt4'
  449. elif mark_name == "余海涛":
  450. sheet = 'R1jIeT'
  451. elif mark_name == "罗情":
  452. sheet = 'iuxfAt'
  453. current_time = datetime.now()
  454. formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S")
  455. if platform == "常规":
  456. third_chars = [j[2] for j in video_files]
  457. data = ",".join(third_chars)
  458. user_id = user
  459. else:
  460. user_id = video_id
  461. data = ''
  462. values = [[mark, str(uid), str(user_id), data, title, new_video_id, formatted_time]]
  463. Feishu.insert_columns("LAn9so7E0hxRYht2UMEcK5wpnMj", sheet, "ROWS", 1, 2)
  464. random_wait_time = random.uniform(0.5, 2.5)
  465. time.sleep(random_wait_time)
  466. Feishu.update_values("LAn9so7E0hxRYht2UMEcK5wpnMj", sheet, "A2:Z2", values)
  467. except Exception as e:
  468. Common.logger("bk_video").warning(f"{mark}的视频拼接失败:{e}\n")
  469. # 清空所有mp4数据
  470. cls.clear_mp4_files(mark)
  471. continue
  472. if "-" in mark:
  473. name = mark.split("-")[0]
  474. else:
  475. name = mark
  476. Feishu.bot('recommend', 'AGC完成通知', f'今日{platform}任务拼接任务完成,共{count}条', name, mark_name)