openshot_video_generator.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  1. from os import listdir
  2. from os.path import isfile, isdir, join
  3. import openshot
  4. import threading
  5. import zhtts
  6. import os
  7. import urllib
  8. from typing import List
  9. import requests
  10. from pydantic import BaseModel
  11. from bs4 import BeautifulSoup
  12. from PIL import Image,ImageDraw,ImageFont
  13. import pyttsx3
  14. import rpyc
  15. import random
  16. import re
  17. import time
  18. import math
  19. import dataset
  20. from datetime import datetime
  21. from gtts import gTTS
  22. import cv2
  23. from mail import mail_to_users
  24. dir_sound = 'mp3_track/'
  25. dir_photo = 'photo/'
  26. dir_text = 'text_file/'
  27. dir_video = 'video_material/'
  28. dir_title = 'title/'
  29. dir_subtitle = 'subtitle/'
  30. dir_anchor = 'anchor_raw/'
  31. tmp_video_dir = 'tmp_video/'
  32. video_sub_folder = 'ai_anchor_video/'
  33. dir_list = [dir_sound,dir_photo,dir_text,dir_video,dir_title,dir_subtitle,dir_anchor,tmp_video_dir]
  34. def notify_group(msg, glist=['7vilzohcyQMPLfAMRloUawiTV4vtusZhxv8Czo7AJX8','WekCRfnAirSiSxALiD6gcm0B56EejsoK89zFbIaiZQD','1dbtJHbWVbrooXmQqc4r8OyRWDryjD4TMJ6DiDsdgsX','HOB1kVNgIb81tTB4Ort1BfhVp9GFo6NlToMQg88vEhh']):
  35. for gid in glist:
  36. headers = {
  37. "Authorization": "Bearer " + gid,
  38. "Content-Type": "application/x-www-form-urlencoded"
  39. }
  40. params = {"message": msg}
  41. r = requests.post("https://notify-api.line.me/api/notify",headers=headers, params=params)
  42. def get_mp4_duration(video_name='mp4-test-file.mp4'): # 可輸入url
  43. cap = cv2.VideoCapture(video_name)
  44. # 幀率
  45. fps = int(round(cap.get(cv2.CAP_PROP_FPS)))
  46. # 分辨率-寬度
  47. width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
  48. # 分辨率-高度
  49. height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
  50. # 總幀數
  51. frame_counter = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
  52. cap.release()
  53. cv2.destroyAllWindows()
  54. # 時長,單位:秒(s)
  55. duration = int(frame_counter / fps)
  56. return duration
  57. def cKey(r,g,b,fuzz):
  58. col=openshot.Color()
  59. col.red=openshot.Keyframe(r)
  60. col.green=openshot.Keyframe(g)
  61. col.blue=openshot.Keyframe(b)
  62. return openshot.ChromaKey(col, openshot.Keyframe(fuzz))
  63. def video_photo_clip(vid=None,layer=None, position=None, end=None
  64. ,scale_x=1,scale_y=1,location_x=0,location_y=0,ck=None,audio=True):
  65. clip = openshot.Clip(vid)
  66. clip.Layer(layer)
  67. clip.Position(position)
  68. clip.End(end)
  69. clip.scale_x=openshot.Keyframe(scale_x)
  70. clip.scale_y=openshot.Keyframe(scale_y)
  71. clip.location_x=openshot.Keyframe(location_x)
  72. clip.location_y=openshot.Keyframe(location_y)
  73. if ck!=None:
  74. clip.AddEffect(ck)
  75. if audio==True:
  76. clip.has_audio=openshot.Keyframe(1)
  77. else:
  78. clip.has_audio=openshot.Keyframe(0)
  79. return clip
  80. def myunichchar(unicode_char):
  81. mb_string = unicode_char.encode('big5')
  82. try:
  83. unicode_char = unichr(ord(mb_string[0]) << 8 | ord(mb_string[1]))
  84. except NameError:
  85. unicode_char = chr(mb_string[0] << 8 | mb_string[1])
  86. return unicode_char
  87. def get_url_type(url):
  88. req = urllib.request.Request(url, method='HEAD', headers={'User-Agent': 'Mozilla/5.0'})
  89. r = urllib.request.urlopen(req)
  90. contentType = r.getheader('Content-Type')
  91. return contentType
  92. def make_dir(name_hash):
  93. for direct in dir_list:
  94. if not os.path.isdir(direct):
  95. os.mkdir(direct)
  96. try:
  97. os.mkdir(dir_photo+name_hash)
  98. except FileExistsError:
  99. print("~~~~~~Warning~~~~~~~~~Directory " , dir_photo+name_hash , " already exists")
  100. try:
  101. os.mkdir(dir_text+name_hash)
  102. except FileExistsError:
  103. print("~~~~~~Warning~~~~~~~~~Directory " , dir_text+name_hash , " already exists")
  104. try:
  105. os.mkdir(dir_sound+name_hash)
  106. except FileExistsError:
  107. print("~~~~~~Warning~~~~~~~~~Directory " , dir_sound+name_hash , " already exists")
  108. try:
  109. os.mkdir(dir_anchor+name_hash)
  110. except FileExistsError:
  111. print("~~~~~~Warning~~~~~~~~~Directory " , dir_anchor+name_hash , " already exists")
  112. try:
  113. os.mkdir(dir_subtitle+name_hash)
  114. except FileExistsError:
  115. print("~~~~~~Warning~~~~~~~~~Directory " , dir_subtitle+name_hash , " already exists")
  116. def file_prepare(name, name_hash,text_content,image_urls,lang='zh'):
  117. make_dir(name_hash)
  118. img_num = 1
  119. for imgu in image_urls:
  120. if get_url_type(imgu) =='video/mp4':
  121. r=requests.get(imgu)
  122. f=open(dir_photo+name_hash+"/"+str(img_num)+".mp4",'wb')
  123. for chunk in r.iter_content(chunk_size=255):
  124. if chunk:
  125. f.write(chunk)
  126. f.close()
  127. else:
  128. im = Image.open(requests.get(imgu, stream=True).raw)
  129. im= im.convert("RGB")
  130. im.save(dir_photo+name_hash+"/"+str(img_num)+".jpg")
  131. img_num+=1
  132. #save text
  133. txt_idx=0
  134. for txt in text_content:
  135. text_file = open(dir_text+name_hash+"/"+str(txt_idx)+".txt", "w")
  136. text_file.write(txt)
  137. text_file.close()
  138. txt_idx+=1
  139. print("text file made")
  140. #make mp3
  141. language = 'zh-tw'
  142. txt_idx = 0
  143. for txt in text_content:
  144. if lang==1:
  145. tts = gTTS(txt)
  146. tts.save(dir_sound+name_hash+"/"+str(txt_idx)+".mp3")
  147. else:
  148. tts = zhtts.TTS()
  149. tts.text2wav(txt,dir_sound+name_hash+"/"+str(txt_idx)+".mp3")
  150. txt_idx+=1
  151. print("mp3 file made")
  152. #make title as image
  153. txt2image_title(name, dir_title+name_hash+".png",lang)
  154. def txt2image(content, save_target,lang='zh'):
  155. unicode_text = trim_punctuation(content)
  156. font = ''
  157. if lang=='zh':
  158. font = ImageFont.truetype(font="font/DFT_B7.ttc", size=38)
  159. else :
  160. font = ImageFont.load("arial.pil")
  161. text_width, text_height = font.getsize(unicode_text)
  162. canvas = Image.new('RGBA', (700, 500), (255, 0, 0, 0) )
  163. draw = ImageDraw.Draw(canvas)
  164. text= unicode_text
  165. draw.text((5,5), text, (255, 255, 0), font)
  166. canvas.save(save_target, "PNG")
  167. def txt2image_title(content, save_target, lang='zh'):
  168. unicode_text = trim_punctuation(content)
  169. font = ''
  170. if lang=='zh':
  171. font = ImageFont.truetype(font="font/DFT_B7.ttc", size=38)
  172. else :
  173. font = ImageFont.load("arial.pil")
  174. text_width, text_height = font.getsize(unicode_text)
  175. canvas = Image.new('RGBA', (510, 500), (255, 0, 0, 0) )
  176. draw = ImageDraw.Draw(canvas)
  177. text= unicode_text
  178. draw.text((5,5), text, (17, 41, 167), font)
  179. canvas.save(save_target, "PNG")
  180. def call_anchor(fileName,avatar):
  181. conn = rpyc.classic.connect("192.168.1.105",18812)
  182. ros = conn.modules.os
  183. rsys = conn.modules.sys
  184. fr=open(dir_sound+fileName+".mp3",'rb')# voice
  185. #warning!!! file my be replaced by other process
  186. fw=conn.builtins.open('/tmp/output.mp3','wb')
  187. while True:
  188. b=fr.read(1024)
  189. if b:
  190. fw.write(b)
  191. else:
  192. break
  193. fr.close()
  194. fw.close()
  195. val=random.randint(1000000,9999999)
  196. ros.chdir('/home/jared/to_video')
  197. ros.system('./p'+str(avatar)+'.sh '+str(val)+' &')
  198. while True:
  199. print('waiting...')
  200. if ros.path.exists('/tmp/results/'+str(val)):
  201. break
  202. time.sleep(5)
  203. print('waiting...')
  204. fr=conn.builtins.open('/tmp/results/'+str(val)+'.mp4','rb')
  205. fw=open(dir_anchor+fileName+".mp4",'wb')
  206. while True:
  207. b=fr.read(1024)
  208. if b:
  209. fw.write(b)
  210. else:
  211. break
  212. fr.close()
  213. fw.close()
  214. def trim_punctuation(s):
  215. pat_block = u'[^\u4e00-\u9fff0-9a-zA-Z]+';
  216. pattern = u'([0-9]+{0}[0-9]+)|{0}'.format(pat_block)
  217. res = re.sub(pattern, lambda x: x.group(1) if x.group(1) else u" " ,s)
  218. return res
  219. def splitter(s):
  220. for sent in re.findall(u'[^!?,。\!\?]+[!?。\!\?]?', s, flags=re.U):
  221. yield sent
  222. def split_by_pun(s):
  223. res = list(splitter(s))
  224. return res
  225. def generate_subtitle_image(name_hash,text_content):
  226. img_list = [None]*len(text_content)
  227. for idx in range(len(text_content)):
  228. img_list[idx]=[]
  229. senList = split_by_pun(text_content[idx])
  230. for inner_idx in range(len(senList)):
  231. sv_path = dir_subtitle + name_hash +'/'+str(idx)+ str(inner_idx) +'.png'
  232. sub = senList[inner_idx]
  233. txt2image(sub,sv_path)
  234. img_list[idx]+=[{"count":len(sub),"path":sv_path}]
  235. return img_list
  236. def generate_subtitle_image_ENG(name_hash,text_content):
  237. img_list = [None]*len(text_content)
  238. for idx in range(len(text_content)):
  239. sv_path = dir_subtitle + name_hash +'/'+str(idx)+'.png'
  240. sub = text_content[idx]
  241. txt2image(sub, sv_path,lang='eng')
  242. img_list[idx] = sv_path
  243. return img_list
  244. def anchor_video_v2(name_hash,name,text_content, image_urls,avatar):
  245. """ 影片產生主程式。 """
  246. print(os.getcwd())
  247. print('sub image made')
  248. file_prepare(name, name_hash, text_content,image_urls,0)
  249. sub_list=generate_subtitle_image(name_hash,text_content)
  250. for fname in range(len(text_content)):
  251. call_anchor(name_hash+"/"+str(fname),avatar)
  252. print('step finish')
  253. print('called............................................')
  254. ck=cKey(0,254,0,270)
  255. ck_anchor=cKey(0,255,1,320)
  256. duration = 0
  257. #average layer level is 3
  258. t = openshot.Timeline(1280, 720, openshot.Fraction(30000, 1000), 44100, 2, openshot.LAYOUT_STEREO)
  259. t.Open()
  260. main_timer = 0
  261. LOGO_OP = openshot.FFmpegReader(dir_video+"LOGO_OP.mp4")
  262. LOGO_OP.Open() # Open the reader
  263. LOGO_OP_clip = video_photo_clip(vid=LOGO_OP,layer=4,position=0,end=LOGO_OP.info.duration
  264. ,location_y=-0.03,scale_x=0.8,scale_y=0.704)
  265. t.AddClip(LOGO_OP_clip)
  266. bg_head = openshot.FFmpegReader(dir_video+"bg_head.avi")
  267. bg_head.Open()
  268. bg_head_clip = video_photo_clip(vid=bg_head,layer=2,position=0,end=LOGO_OP.info.duration,ck=ck)
  269. t.AddClip(bg_head_clip)
  270. main_timer += LOGO_OP.info.duration
  271. head_duration = LOGO_OP.info.duration
  272. bg_head.Close()
  273. LOGO_OP.Close()
  274. clip_duration=0
  275. photo_clip_list = [None]*len(text_content)
  276. img_list = [None]*len(text_content)
  277. anchor_clip_list = [None] * len(text_content)
  278. anchor_list = [None] * len(text_content)
  279. audio_clip_list = [None] * len(text_content)
  280. audio_list = [None] * len(text_content)
  281. sub_clip_list = [None] * len(text_content)
  282. sub_img_list = [None] * len(text_content)
  283. idx = 0
  284. for p in listdir(dir_photo+name_hash):
  285. anchor_list[idx] = openshot.FFmpegReader(dir_anchor+name_hash+"/"+str(idx)+".mp4")
  286. clip_duration = anchor_list[idx].info.duration
  287. anchor_list[idx].Open()
  288. anchor_clip_list[idx] = video_photo_clip(vid=anchor_list[idx],layer=4,scale_x=0.65,scale_y=0.65,
  289. location_x=0.35,location_y=0.25,position=main_timer, end=clip_duration,ck=ck_anchor,audio=False)
  290. t.AddClip(anchor_clip_list[idx])
  291. img_list[idx] = openshot.FFmpegReader(dir_photo+name_hash+'/'+p)
  292. img_list[idx].Open()
  293. photo_clip_list[idx] = video_photo_clip(vid=img_list[idx],layer=3
  294. ,scale_x=0.81,scale_y=0.68,location_y=-0.03,position=main_timer,end=clip_duration,audio=False)
  295. t.AddClip(photo_clip_list[idx])
  296. img_list[idx].Close()
  297. audio_list[idx] = openshot.FFmpegReader(dir_sound+name_hash+"/"+str(idx)+".mp3")
  298. audio_list[idx].Open()
  299. audio_clip_list[idx] = openshot.Clip(audio_list[idx])
  300. audio_clip_list[idx].Position(main_timer)
  301. audio_clip_list[idx].End(clip_duration)
  302. t.AddClip(audio_clip_list[idx])
  303. img_list[idx].Close()
  304. anchor_list[idx].Close()
  305. audio_list[idx].Close()
  306. sub_img_list[idx] = [None] * len(sub_list[idx])
  307. sub_clip_list[idx] = [None] * len(sub_list[idx])
  308. sub_timer = 0
  309. for sub_idx in range(len(sub_list[idx])):
  310. sub_img_list[idx][sub_idx] = openshot.QtImageReader(sub_list[idx][sub_idx]['path'])
  311. sub_img_list[idx][sub_idx].Open()
  312. sub_duration = 0.205*sub_list[idx][sub_idx]['count']
  313. sub_clip_list[idx][sub_idx] = video_photo_clip(vid=sub_img_list[idx][sub_idx], layer=6,location_x=0.069, location_y=0.89,position=main_timer+sub_timer,end=sub_duration)
  314. t.AddClip(sub_clip_list[idx][sub_idx])
  315. sub_img_list[idx][sub_idx].Close()
  316. sub_timer += sub_duration
  317. print(sub_list[idx][sub_idx]['path'])
  318. main_timer += clip_duration
  319. idx+=1
  320. LOGO_ED = openshot.FFmpegReader(dir_video+"LOGO_ED.avi")
  321. LOGO_ED.Open()
  322. LOGO_ED_clip = video_photo_clip(vid=LOGO_ED,layer=4,position=main_timer,end=LOGO_ED.info.duration+2
  323. ,location_x=0.005,location_y=-0.031
  324. ,scale_x=0.8,scale_y=0.6825)
  325. t.AddClip(LOGO_ED_clip)
  326. ED_duration = LOGO_ED.info.duration
  327. LOGO_ED.Close()
  328. bg = openshot.FFmpegReader(dir_video+"bg.mp4")
  329. bg.Open()
  330. bg_times = math.floor(main_timer+ED_duration/bg.info.duration)
  331. left_time = (main_timer+ED_duration) % bg.info.duration
  332. bg_clip_list = [None] * bg_times
  333. bg_list = [None] * bg_times
  334. bg.Close()
  335. bg_timer = head_duration
  336. for idx in range(bg_times):
  337. bg_list[idx] = openshot.FFmpegReader(dir_video+"bg.mp4")
  338. bg_list[idx].Open()
  339. bg_clip_list[idx] = video_photo_clip(bg_list[idx],layer=2,position=bg_timer
  340. ,end=bg_list[idx].info.duration,ck=ck)
  341. t.AddClip(bg_clip_list[idx])
  342. bg_timer += bg_list[idx].info.duration
  343. bg_list[idx].Close()
  344. bg_left = openshot.FFmpegReader(dir_video+"bg.mp4")
  345. bg_left.Open()
  346. bg_left_clip = video_photo_clip(bg_left,layer=2,position=bg_timer,end=left_time,ck=ck)
  347. t.AddClip(bg_left_clip)
  348. bg_left.Close()
  349. title = openshot.QtImageReader(dir_title+name_hash+".png")
  350. title.Open() # Open the reader
  351. title_clip = video_photo_clip(vid=title, layer=4,location_x=-0.047, location_y=0.801,position=0,end=head_duration+main_timer)
  352. t.AddClip(title_clip)
  353. ####start building
  354. w = openshot.FFmpegWriter(tmp_video_dir+name_hash+".mp4")
  355. w.SetAudioOptions(True, "aac", 44100, 2, openshot.LAYOUT_STEREO, 3000000)
  356. w.SetVideoOptions(True, "libx264", openshot.Fraction(30000, 1000), 1280, 720,
  357. openshot.Fraction(1, 1), False, False, 3000000)
  358. w.Open()
  359. #may change duration into t.info.duration
  360. frames = int(t.info.fps)*int(head_duration+main_timer+ED_duration)
  361. for n in range(frames):
  362. f=t.GetFrame(n)
  363. w.WriteFrame(f)
  364. # 更新剩下時間、duration
  365. video_link = f"www.choozmo.com:8168/{video_sub_folder}{name_hash}.mp4"
  366. duration, user_id = update_hisotry_duration(video_link)
  367. is_left_time = update_user_left_time(user_id, duration)
  368. # 1.餘額足 2. 餘額不足
  369. if is_left_time:
  370. notify_group(name+"的影片已經產生完成囉! www.choozmo.com:8168/"+video_sub_folder+name_hash+".mp4")
  371. contents = f""" 影片下載網址: {video_link}"""
  372. mail_to_users(user_id, f'您好,您的影片 "{name}" 已經完成', contents=contents)
  373. else:
  374. notify_group(msg="您的餘額不足,請去...儲值,才能取得影片唷!")
  375. contents = f""" 餘額不足,請往: ...儲值url儲值 """
  376. mail_to_users(user_id, f'您好,您的餘額不足', contents)
  377. t.Close()
  378. w.Close()
  379. print("video at : www.choozmo.com:8168/"+video_sub_folder+name_hash+".mp4")
  380. def anchor_video_eng(name_hash,name,text_content, image_urls,sub_titles,avatar):
  381. file_prepare(name, name_hash, text_content,image_urls,'eng')
  382. sub_list=generate_subtitle_image_ENG(name_hash,sub_titles)
  383. for fname in range(len(text_content)):
  384. call_anchor(name_hash+"/"+str(fname),avatar)
  385. print('step finish')
  386. print('called............................................')
  387. ck=cKey(0,254,0,270)
  388. ck_anchor=cKey(0,255,1,320)
  389. duration = 0
  390. #average layer level is 3
  391. t = openshot.Timeline(1280, 720, openshot.Fraction(30000, 1000), 44100, 2, openshot.LAYOUT_STEREO)
  392. t.Open()
  393. main_timer = 0
  394. #add logo
  395. LOGO_OP = openshot.FFmpegReader(dir_video+"LOGO_OP.mp4")
  396. LOGO_OP.Open() # Open the reader
  397. LOGO_OP_clip = video_photo_clip(vid=LOGO_OP,layer=4,position=0,end=LOGO_OP.info.duration
  398. ,location_y=-0.03,scale_x=0.8,scale_y=0.704)
  399. t.AddClip(LOGO_OP_clip)
  400. #add background video (head is different)
  401. bg_head = openshot.FFmpegReader(dir_video+"bg_head_eng.mp4")
  402. bg_head.Open()
  403. bg_head_clip = video_photo_clip(vid=bg_head,layer=2,position=0,end=LOGO_OP.info.duration,ck=ck)
  404. t.AddClip(bg_head_clip)
  405. main_timer += LOGO_OP.info.duration
  406. head_duration = LOGO_OP.info.duration
  407. bg_head.Close()
  408. LOGO_OP.Close()
  409. #prepare empty list
  410. clip_duration=0
  411. photo_clip_list = [None]*len(text_content)
  412. img_list = [None]*len(text_content)
  413. anchor_clip_list = [None] * len(text_content)
  414. anchor_list = [None] * len(text_content)
  415. audio_clip_list = [None] * len(text_content)
  416. audio_list = [None] * len(text_content)
  417. sub_clip_list = [None] * len(text_content)
  418. #openshot image holder
  419. sub_img_list = [None] * len(text_content)
  420. idx = 0
  421. for p in listdir(dir_photo+name_hash):
  422. anchor_list[idx] = openshot.FFmpegReader(dir_anchor+name_hash+"/"+str(idx)+".mp4")
  423. clip_duration = anchor_list[idx].info.duration
  424. anchor_list[idx].Open()
  425. anchor_clip_list[idx] = video_photo_clip(vid=anchor_list[idx],layer=4,scale_x=0.65,scale_y=0.65,
  426. location_x=0.35,location_y=0.25,position=main_timer, end=clip_duration,ck=ck_anchor,audio=False)
  427. t.AddClip(anchor_clip_list[idx])
  428. #insert image
  429. img_list[idx] = openshot.FFmpegReader(dir_photo+name_hash+'/'+p)
  430. img_list[idx].Open()
  431. photo_clip_list[idx] = video_photo_clip(vid=img_list[idx],layer=3
  432. ,scale_x=0.81,scale_y=0.68,location_y=-0.03,position=main_timer,end=clip_duration,audio=False)
  433. t.AddClip(photo_clip_list[idx])
  434. img_list[idx].Close()
  435. #insert audio (speech)
  436. audio_list[idx] = openshot.FFmpegReader(dir_sound+name_hash+"/"+str(idx)+".mp3")
  437. audio_list[idx].Open()
  438. audio_clip_list[idx] = openshot.Clip(audio_list[idx])
  439. audio_clip_list[idx].Position(main_timer)
  440. audio_clip_list[idx].End(clip_duration)
  441. t.AddClip(audio_clip_list[idx])
  442. #insert subtitle
  443. sub_img_list[idx] = openshot.QtImageReader(sub_list[idx])
  444. sub_img_list[idx].Open()
  445. sub_clip_list[idx] = video_photo_clip(vid=sub_img_list[idx], layer=6,location_x=0.069, location_y=0.89,position=main_timer,end=clip_duration)
  446. t.AddClip(sub_clip_list[idx])
  447. img_list[idx].Close()
  448. anchor_list[idx].Close()
  449. audio_list[idx].Close()
  450. sub_img_list[idx].Close()
  451. main_timer += clip_duration
  452. idx+=1
  453. LOGO_ED = openshot.FFmpegReader(dir_video+"ED_ENG.mp4")
  454. LOGO_ED.Open()
  455. LOGO_ED_clip = video_photo_clip(vid=LOGO_ED,layer=4,position=main_timer,end=LOGO_ED.info.duration+2
  456. ,location_x=0.005,location_y=-0.031
  457. ,scale_x=0.8,scale_y=0.6825)
  458. t.AddClip(LOGO_ED_clip)
  459. ED_duration = LOGO_ED.info.duration
  460. LOGO_ED.Close()
  461. bg = openshot.FFmpegReader(dir_video+"bg_eng.mp4")
  462. bg.Open()
  463. bg_times = math.floor(main_timer+ED_duration/bg.info.duration)
  464. left_time = (main_timer+ED_duration) % bg.info.duration
  465. bg_clip_list = [None] * bg_times
  466. bg_list = [None] * bg_times
  467. bg.Close()
  468. bg_timer = head_duration
  469. for idx in range(bg_times):
  470. bg_list[idx] = openshot.FFmpegReader(dir_video+"bg_eng.mp4")
  471. bg_list[idx].Open()
  472. bg_clip_list[idx] = video_photo_clip(bg_list[idx],layer=2,position=bg_timer
  473. ,end=bg_list[idx].info.duration,ck=ck)
  474. t.AddClip(bg_clip_list[idx])
  475. bg_timer += bg_list[idx].info.duration
  476. bg_list[idx].Close()
  477. bg_left = openshot.FFmpegReader(dir_video+"bg_eng.mp4")
  478. bg_left.Open()
  479. bg_left_clip = video_photo_clip(bg_left,layer=2,position=bg_timer,end=left_time,ck=ck)
  480. t.AddClip(bg_left_clip)
  481. bg_left.Close()
  482. title = openshot.QtImageReader(dir_title+name_hash+".png")
  483. title.Open() # Open the reader
  484. title_clip = video_photo_clip(vid=title, layer=4,location_x=-0.047, location_y=0.801,position=0,end=head_duration+main_timer)
  485. t.AddClip(title_clip)
  486. ####start building
  487. w = openshot.FFmpegWriter(tmp_video_dir+name_hash+".mp4")
  488. w.SetAudioOptions(True, "aac", 44100, 2, openshot.LAYOUT_STEREO, 3000000)
  489. w.SetVideoOptions(True, "libx264", openshot.Fraction(30000, 1000), 1280, 720,
  490. openshot.Fraction(1, 1), False, False, 3000000)
  491. w.Open()
  492. #may change duration into t.info.duration
  493. frames = int(t.info.fps)*int(head_duration+main_timer+ED_duration)
  494. for n in range(frames):
  495. f=t.GetFrame(n)
  496. w.WriteFrame(f)
  497. # 更新剩下時間、duration
  498. video_link = f"www.choozmo.com:8168/{video_sub_folder}{name_hash}.mp4"
  499. duration, user_id = update_hisotry_duration(video_link)
  500. is_left_time = update_user_left_time(user_id, duration)
  501. # 1.餘額足 2. 餘額不足
  502. if is_left_time:
  503. notify_group(name+", Your video is complete! www.choozmo.com:8168/"+video_sub_folder+name_hash+".mp4")
  504. contents = f""" The download address: {video_link}"""
  505. mail_to_users(user_id, f'Hi, your video "{name}" is complete', contents=contents)
  506. else:
  507. notify_group(msg="The left money is not enough, please deposit to get the video!")
  508. contents = f""" The left money is not enough, please go to ... to deposit. """
  509. mail_to_users(user_id, f'Hi, your remain deposit is not enough', contents)
  510. t.Close()
  511. w.Close()
  512. print("video at : www.choozmo.com:8168/"+video_sub_folder+name_hash+".mp4")
  513. #line notifs
  514. class video_service(rpyc.Service):
  515. def exposed_call_video(self,name_hash,name,text_content, image_urls,avatar):
  516. anchor_video_v2(name_hash,name,text_content, image_urls,avatar)
  517. def exposed_call_video_eng(self,name_hash,name,text_content, image_urls,sub_titles,avatar):
  518. anchor_video_eng(name_hash,name,text_content, image_urls,sub_titles,avatar)
  519. def update_hisotry_duration(video_link):
  520. """ 更新資訊影片長度資訊。 """
  521. db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/AI_anchor?charset=utf8mb4')
  522. table = db['history_input']
  523. duration = get_mp4_duration(video_link)
  524. data = dict(link=video_link, duration=duration)
  525. table.update(data, ['link'])
  526. rows = db.query(f'SELECT * FROM history_input WHERE link="{video_link}"')
  527. for row in rows:
  528. user_id = row['user_id']
  529. return duration, user_id
  530. def update_user_left_time(user_id, duration):
  531. """ 更新使用者剩下時間,如果為負,接著提醒。 """
  532. db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/AI_anchor?charset=utf8mb4')
  533. table = db['users']
  534. rows = db.query(f"SELECT * FROM user WHERE id={user_id}")
  535. for row in rows:
  536. left_time = row['left_time']
  537. line_token = row['line_token']
  538. left_time -= duration
  539. data = dict(id=user_id, left_time=left_time-duration)
  540. table.update(data, ['id'])
  541. if left_time < 0:
  542. return False
  543. else:
  544. return True
  545. from rpyc.utils.server import ThreadedServer
  546. t = ThreadedServer(video_service, port=8878)
  547. print('service started')
  548. t.start()