video_prep.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. import configparser
  2. import os
  3. import random
  4. import re
  5. import sys
  6. import threading
  7. import time
  8. from datetime import datetime
  9. import concurrent.futures
  10. from common.tts_help import TTS
  11. sys.path.append(os.getcwd())
  12. from common import Material, Feishu, Common, Oss
  13. from common.ffmpeg import FFmpeg
  14. from common.gpt4o_help import GPT4o
  15. from data_channel.douyin import DY
  16. from data_channel.kuaishou import KS
  17. from data_channel.kuaishouchuangzuozhe import KsFeedVideo
  18. from data_channel.piaoquan import PQ
  19. from common.sql_help import sqlCollect
  20. from data_channel.shipinhao import SPH
  21. config = configparser.ConfigParser()
  22. config.read('./config.ini')
  23. class getVideo:
  24. """
  25. 根据标示+任务标示创建目录
  26. """
  27. @classmethod
  28. def create_folders(cls, mark, task_mark):
  29. video_path_url = config['PATHS']['VIDEO_PATH'] + mark + "/" + task_mark + "/"
  30. if not os.path.exists(video_path_url):
  31. os.makedirs(video_path_url)
  32. return video_path_url
  33. """
  34. 随机生成ID
  35. """
  36. @classmethod
  37. def random_id(cls):
  38. now = datetime.now()
  39. rand_num = random.randint(10000, 99999)
  40. oss_id = "{}{}".format(now.strftime("%Y%m%d%H%M%S"), rand_num)
  41. return oss_id
  42. """
  43. 删除文件
  44. """
  45. @classmethod
  46. def remove_files(cls, video_path_url):
  47. if os.path.exists(video_path_url) and os.path.isdir(video_path_url):
  48. for root, dirs, files in os.walk(video_path_url):
  49. for file in files:
  50. file_path = os.path.join(root, file)
  51. os.remove(file_path)
  52. for dir in dirs:
  53. dir_path = os.path.join(root, dir)
  54. os.rmdir(dir_path)
  55. """
  56. 飞书数据处理
  57. """
  58. @classmethod
  59. def video_task(cls, data):
  60. mark = data["mark"]
  61. name = data["name"]
  62. feishu_id = data["feishu_id"]
  63. feishu_sheet = data["feishu_sheet"]
  64. cookie_sheet = data["cookie_sheet"]
  65. pz_sheet = '500Oe0'
  66. pw_sheet = 'DgX7vC'
  67. task_data = Material.get_task_data(feishu_id, feishu_sheet)
  68. # task_data = task_data[::-1]
  69. if len(task_data) == 0:
  70. Feishu.bot(mark, '机器自动改造消息通知', f'今日任务为空,请关注', name)
  71. return mark
  72. lock = threading.Lock()
  73. def process_task(task):
  74. task_mark = task["task_mark"] # 任务标示
  75. channel_id = str(task["channel_id"])
  76. channel_urls = str(task["channel_url"])
  77. piaoquan_id = str(task["piaoquan_id"])
  78. number = task["number"] # 指定条数
  79. title = task["title"]
  80. video_share = task["video_share"]
  81. video_ending = task["video_ending"]
  82. crop_total = task["crop_total"]
  83. gg_duration_total = task["gg_duration_total"]
  84. video_path_url = cls.create_folders(mark, str(task_mark)) # 创建目录
  85. zm = Material.get_pzsrt_data("summary", pz_sheet, video_share) # 获取srt
  86. if zm == '':
  87. Feishu.bot(mark, '机器自动改造消息通知', f'{task_mark}任务下片中标示填写错误,请关注!!!!', name)
  88. if ',' in channel_urls:
  89. channel_url = channel_urls.split(',')
  90. else:
  91. channel_url = [channel_urls]
  92. for url in channel_url:
  93. Common.logger("log").info(f"{name}的{task_mark}下的用户:{channel_url}开始获取视频")
  94. if channel_id == "抖音":
  95. data_list = DY.get_dy_url(task_mark, url, number, mark, feishu_id, cookie_sheet, channel_id, name)
  96. elif channel_id == "票圈":
  97. data_list = PQ.get_pq_url(task_mark, url, number, mark)
  98. elif channel_id == "视频号":
  99. data_list = SPH.get_sph_url(task_mark, url, number, mark)
  100. elif channel_id == "快手":
  101. data_list = KS.get_ks_url(task_mark, url, number, mark, feishu_id, cookie_sheet, channel_id, name)
  102. elif channel_id == "快手创作者版":
  103. data_list = KsFeedVideo.get_data(number)
  104. if len(data_list) == 0:
  105. Common.logger("log").info(f"{name}的{task_mark}下的视频ID{url} 已经改造过了")
  106. Feishu.bot("快手创作者版", '机器自动改造消息通知', f'本轮没有获取到改造的视频链接', "王雪珂")
  107. cls.remove_files(video_path_url)
  108. continue
  109. if len(data_list) == 0:
  110. Common.logger("log").info(f"{name}的{task_mark}下的视频ID{url} 已经改造过了")
  111. Feishu.bot(mark, '机器自动改造消息通知', f'{task_mark}任务下的用户ID{url},没有已经改造的视频了', name)
  112. cls.remove_files(video_path_url)
  113. continue
  114. Common.logger("log").info(f"{name}的{task_mark}下的ID{url} 获取视频完成,共{len(data_list)}条")
  115. try:
  116. for video in data_list:
  117. new_title = video['old_title'].strip().replace("\n", "") \
  118. .replace("/", "").replace("\\", "").replace("\r", "") \
  119. .replace(":", "").replace("*", "").replace("?", "") \
  120. .replace("?", "").replace('"', "").replace("<", "") \
  121. .replace(">", "").replace("|", "").replace(" ", "") \
  122. .replace("&NBSP", "").replace(".", "。").replace(" ", "") \
  123. .replace("'", "").replace("#", "").replace("Merge", "")
  124. if title == "原标题":
  125. if new_title == "" or len(new_title) == 0 or new_title == None:
  126. new_title = '⭕分享给大家一个视频!值得细品!'
  127. elif title == "AI标题":
  128. if new_title == "" or len(new_title) == 0 or new_title == None:
  129. new_title = '⭕分享给大家一个视频!值得细品!'
  130. else:
  131. new_title = GPT4o.get_ai_title(new_title)
  132. else:
  133. if '/' in title:
  134. titles = title.split('/')
  135. else:
  136. titles = [title]
  137. new_title = random.choice(titles)
  138. v_id = video["video_id"]
  139. cover = video["cover"]
  140. video_url = video["video_url"]
  141. rule = video['rule']
  142. old_title = video['old_title']
  143. if old_title == "" or len(old_title) == 0 or old_title == None:
  144. old_title = '⭕分享给大家一个视频!值得细品!'
  145. time.sleep(1)
  146. pw_random_id = cls.random_id()
  147. if channel_id == "票圈" or channel_id == "快手创作者版":
  148. new_video_path = PQ.download_video(video_url, video_path_url, v_id) # 下载视频地址
  149. else:
  150. new_video_path = Oss.download_video_oss(video_url, video_path_url, v_id) # 下载视频地址
  151. Common.logger("log").info(f"{name}的{task_mark}下的视频{url},{new_video_path}视频下载成功")
  152. if not os.path.isfile(new_video_path):
  153. Common.logger("log").info(f"{name}的{task_mark}下的视频{url},{new_video_path}视频下载失败")
  154. cls.remove_files(video_path_url)
  155. continue
  156. Common.logger("log").info(f"{name}的{task_mark}下的视频{url},{new_video_path}视频下载成功")
  157. if crop_total and crop_total != 'None': # 判断是否需要裁剪
  158. new_video_path = FFmpeg.video_crop(new_video_path, video_path_url, pw_random_id)
  159. if gg_duration_total and gg_duration_total != 'None': # 判断是否需要指定视频时长
  160. new_video_path = FFmpeg.video_ggduration(new_video_path, video_path_url, pw_random_id, gg_duration_total)
  161. width, height = FFmpeg.get_w_h_size(new_video_path)
  162. if width < height:
  163. new_video_path = FFmpeg.update_video_h_w(new_video_path, video_path_url, pw_random_id)
  164. time.sleep(1)
  165. if os.path.isfile(new_video_path):
  166. if new_title != "" or len(new_title) != 0 or new_title != None:
  167. new_title_re = re.sub(r'[^\w\s\u4e00-\u9fff,。!?]', '', new_title)
  168. if len(new_title_re) > 12:
  169. new_text = '\n'.join(
  170. [new_title_re[i:i + 12] for i in range(0, len(new_title_re), 12)])
  171. new_video_path = FFmpeg.add_video_zm(new_video_path, video_path_url, pw_random_id, new_text)
  172. if video_ending and video_ending != 'None':
  173. if video_ending == "AI片尾引导":
  174. pw_srt_text = GPT4o.get_ai_pw(old_title)
  175. if pw_srt_text:
  176. pw_url = TTS.get_pw_zm(pw_srt_text)
  177. Common.logger("log").info(f"{name}的{task_mark}下的视频{url},获取AI片尾音频成功")
  178. if pw_url:
  179. pw_mp3_path = TTS.download_mp3(pw_url, video_path_url, pw_random_id)
  180. pw_url_sec = FFmpeg.get_video_duration(pw_mp3_path) # 获取片尾秒数
  181. Common.logger("log").info(f"{name}的{task_mark}下的视频{url},获取AI片尾秒数成功{pw_url_sec}")
  182. pw_srt = TTS.getSrt(pw_url)
  183. Common.logger("log").info(f"{name}的{task_mark}下的视频{url},获取AI片尾srt成功")
  184. else:
  185. Feishu.bot(mark, 'TTS获取失败提示', f'无法获取到片尾音频,及时更换token', "张勇")
  186. Common.logger("log").info(f"{name}的{task_mark}下的视频{url},获取AI片尾失败")
  187. continue
  188. else:
  189. Common.logger("log").info(f"{name}的{task_mark}下的视频{url},获取AI片尾失败")
  190. continue
  191. else:
  192. if ',' in video_ending:
  193. video_ending_list = video_ending.split(',')
  194. else:
  195. video_ending_list = [video_ending]
  196. ending = random.choice(video_ending_list)
  197. pw_list = Material.get_pwsrt_data("summary", pw_sheet, ending) # 获取srt
  198. if pw_list:
  199. pw_id = pw_list["pw_id"]
  200. pw_srt = pw_list["pw_srt"]
  201. pw_url = PQ.get_pw_url(pw_id)
  202. pw_mp3_path = FFmpeg.get_video_mp3(pw_url, video_path_url, pw_random_id)
  203. else:
  204. Feishu.bot(mark, '机器自动改造消息通知', f'{task_mark}任务下片尾标示错误,请关注!!!!', name)
  205. for attempt in range(3):
  206. jpg_path = FFmpeg.video_png(new_video_path, video_path_url, pw_random_id) # 生成视频最后一帧jpg
  207. if os.path.isfile(jpg_path):
  208. Common.logger("log").info(f"{name}的{task_mark}下的视频{url},生成视频最后一帧成功")
  209. break
  210. time.sleep(1)
  211. if not os.path.isfile(jpg_path):
  212. Feishu.bot(mark, '机器自动改造消息通知', f'{task_mark}任务用户{url}下的视频{v_id},获取视频最后一帧失败,请关注', name)
  213. cls.remove_files(video_path_url)
  214. continue
  215. for attempt in range(3):
  216. Common.logger("log").info(f"{name}的{task_mark}下的视频{url},获取mp3成功")
  217. pw_path = FFmpeg.pw_video(jpg_path, video_path_url, pw_mp3_path, pw_srt, pw_random_id, pw_mp3_path) # 生成片尾视频
  218. if os.path.isfile(pw_path):
  219. Common.logger("log").info(f"{task_mark}下的视频{url},生成片尾视频成功")
  220. break
  221. time.sleep(1)
  222. if not os.path.isfile(pw_path):
  223. Feishu.bot(mark, '机器自动改造消息通知', f'{task_mark}任务用户{url}下的视频{v_id},生成片尾视频失败,请关注',
  224. name)
  225. cls.remove_files(video_path_url)
  226. continue
  227. pw_video_list = [new_video_path, pw_path]
  228. Common.logger("log").info(f"{task_mark}下的视频{url},视频与片尾开始拼接")
  229. video_path = FFmpeg.concatenate_videos(pw_video_list, video_path_url) # 视频与片尾拼接到一起
  230. Common.logger("log").info(f"{name}的{task_mark}下的视频{url},视频与片尾拼接成功")
  231. time.sleep(1)
  232. if video_share and video_share != 'None':
  233. new_video_path = FFmpeg.single_video(video_path, video_path_url, zm)
  234. else:
  235. new_video_path = video_path
  236. else:
  237. if video_share and video_share != 'None':
  238. new_video_path = FFmpeg.single_video(new_video_path, video_path_url, zm)
  239. time.sleep(1)
  240. oss_id = cls.random_id()
  241. Common.logger("log").info(f"{name}的{task_mark}下的视频{url},开始发送oss")
  242. oss_object_key = Oss.stitching_sync_upload_oss(new_video_path, oss_id) # 视频发送OSS
  243. Common.logger("log").info(f"{name}的{task_mark}下的视频{url},发送oss成功{oss_object_key}")
  244. status = oss_object_key.get("status")
  245. if status == 200:
  246. oss_object_key = oss_object_key.get("oss_object_key")
  247. time.sleep(1)
  248. jpg_path = PQ.download_video_jpg(cover, video_path_url, v_id) # 下载视频封面
  249. if os.path.isfile(jpg_path):
  250. oss_jpg_key = Oss.stitching_fm_upload_oss(jpg_path, oss_id) # 封面发送OSS
  251. status = oss_jpg_key.get("status")
  252. if status == 200:
  253. jpg = oss_jpg_key.get("oss_object_key")
  254. else:
  255. jpg = None
  256. else:
  257. jpg = None
  258. code = PQ.insert_piaoquantv(oss_object_key, new_title, jpg, piaoquan_id)
  259. if code:
  260. Common.logger("log").info(f"{name}的{task_mark}下的视频ID{v_id}发送成功")
  261. sqlCollect.insert_task(task_mark, v_id, mark, channel_id) # 插入数据库
  262. current_time = datetime.now()
  263. formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S")
  264. sqlCollect.insert_machine_making_data(name, task_mark, channel_id, url, v_id, piaoquan_id, new_title, code, formatted_time, old_title)
  265. if title == "原标题" or title == "AI标题":
  266. values = [[name, task_mark, channel_id, url, str(v_id), piaoquan_id, old_title, title,new_title, str(code), formatted_time, str(rule)]]
  267. else:
  268. values = [[name, task_mark, channel_id, url, str(v_id), piaoquan_id, old_title, "",new_title, str(code), formatted_time, str(rule)]]
  269. # 使用锁保护表格插入操作
  270. with lock:
  271. if name == "王雪珂":
  272. sheet = "vfhHwj"
  273. elif name == "抖音品类账号-1":
  274. sheet = "61kvW7"
  275. elif name == "鲁涛":
  276. sheet = "FhewlS"
  277. elif name == "范军":
  278. sheet = "B6dCfS"
  279. elif name == "余海涛":
  280. sheet = "mfBrNT"
  281. elif name == "罗情":
  282. sheet = "2J3PwN"
  283. elif name == "王玉婷":
  284. sheet = "bBHFwC"
  285. elif name == "刘诗雨":
  286. sheet = "fBdxIQ"
  287. elif name == "信欣":
  288. sheet = "lPe1eT"
  289. elif name == "快手创作者版品类推荐流":
  290. sheet = "k7l7nQ"
  291. elif name == "抖音品类账号":
  292. sheet = "Bsg5UR"
  293. Feishu.insert_columns("ILb4sa0LahddRktnRipcu2vQnLb", sheet, "ROWS", 1, 2)
  294. time.sleep(0.5)
  295. # time.sleep(random.randint(1, 2))
  296. Feishu.update_values("ILb4sa0LahddRktnRipcu2vQnLb", sheet, "A2:Z2", values)
  297. else:
  298. Common.logger("warning").info(f"{name}的{task_mark}下的{url}视频{v_id} 视频发送票圈失败 {code}")
  299. cls.remove_files(video_path_url)
  300. else:
  301. cls.remove_files(video_path_url)
  302. Common.logger("warning").info(f"{name}的{task_mark}下的{url}视频{v_id} 视频发送OSS失败 ")
  303. Feishu.bot(mark, '机器自动改造消息通知', f'{task_mark}任务改造完成,请关注', name)
  304. except Exception as e:
  305. cls.remove_files(video_path_url)
  306. Common.logger("warning").warning(f"{name}的{task_mark}任务处理失败:{e}\n")
  307. batch_size = 1
  308. with concurrent.futures.ThreadPoolExecutor(max_workers=batch_size) as executor:
  309. index = 0
  310. while index < len(task_data):
  311. # 计算剩余的任务数量
  312. remaining_tasks = len(task_data) - index
  313. # 当前批次大小为剩余任务数量和批次大小中较小的一个
  314. current_batch_size = min(batch_size, remaining_tasks)
  315. # 获取当前批次的任务
  316. current_batch = task_data[index:index + batch_size]
  317. futures = {executor.submit(process_task, task): task for task in current_batch}
  318. for future in concurrent.futures.as_completed(futures):
  319. task = futures[future]
  320. try:
  321. future.result()
  322. print(f"Task {task['task_mark']} 完成")
  323. except Exception as exc:
  324. Common.logger("warning").warning(f"{name}的{task['task_mark']}任务处理失败:{exc}\n")
  325. print(f"Task {task['task_mark']} 异常信息: {exc}")
  326. # 移动到下一批任务
  327. index += current_batch_size
  328. Feishu.bot(mark, '机器自动改造消息通知', f'你的任务全部完成,请关注!!!!!', name)
  329. return mark