main.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. import openshot
  2. import re
  3. from PIL import Image,ImageDraw,ImageFont
  4. import pandas as pd
  5. import os
  6. import cv2
  7. import numpy as np
  8. # import moviepy.editor as mp
  9. import time
  10. import pysrt
  11. import shutil
  12. import rpyc
  13. import random
  14. import string
  15. import requests
  16. from bs4 import BeautifulSoup
  17. import zipfile
  18. import csv
  19. from datetime import datetime
  20. def cKey(r,g,b,fuzz):
  21. col=openshot.Color()
  22. col.red=openshot.Keyframe(r)
  23. col.green=openshot.Keyframe(g)
  24. col.blue=openshot.Keyframe(b)
  25. return openshot.ChromaKey(col, openshot.Keyframe(fuzz))
  26. def video_writer_init(path):
  27. w = openshot.FFmpegWriter(path)
  28. w.SetAudioOptions(True, "aac", 44100, 2, openshot.LAYOUT_STEREO, 3000000)
  29. w.SetVideoOptions(True, "libx264", openshot.Fraction(30000, 1000), 1280, 720,
  30. openshot.Fraction(1, 1), False, False, 3000000)
  31. return w
  32. def video_photo_clip(video=None,layer=None, position=None, end=None
  33. ,scale_x=1,scale_y=1,location_x=0,location_y=0,ck=None,audio=True):
  34. clip = openshot.Clip(video)
  35. clip.Layer(layer)
  36. clip.Position(position)
  37. clip.End(end)
  38. clip.scale_x=openshot.Keyframe(scale_x)
  39. clip.scale_y=openshot.Keyframe(scale_y)
  40. clip.location_x=openshot.Keyframe(location_x)
  41. clip.location_y=openshot.Keyframe(location_y)
  42. if ck!=None:
  43. clip.AddEffect(ck)
  44. if audio==True:
  45. clip.has_audio=openshot.Keyframe(1)
  46. else:
  47. clip.has_audio=openshot.Keyframe(0)
  48. return clip
  49. def trim_punctuation(s):
  50. pat_block = u'[^\u4e00-\u9fff0-9a-zA-Z]+'
  51. pattern = u'([0-9]+{0}[0-9]+)|{0}'.format(pat_block)
  52. res = re.sub(pattern, lambda x: x.group(1) if x.group(1) else u" " ,s)
  53. return res
  54. def randomString(stringLength=10):
  55. letters = string.ascii_lowercase
  56. return ''.join(random.choice(letters) for i in range(stringLength))
  57. def mp3_to_anchor(fname):
  58. conn = rpyc.classic.connect("192.168.192.221",18812)
  59. fr=open(fname,'rb')
  60. ropen = conn.builtins.open
  61. randname=randomString(10)
  62. finalname=randomString(10)
  63. fw=ropen('/tmp/'+randname+'.mp4','wb')
  64. fw.write(fr.read())
  65. fw.close()
  66. ros = conn.modules.os
  67. ros.system('/root/to_video/p9.sh '+randname+".mp4 "+finalname+".mp4")
  68. return 'http://192.168.192.221/video/'+finalname+'.mp4'
  69. # conn.execute('import os')
  70. def download_mp4(url):
  71. headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36'}
  72. with open('input_self/AI_girl/ai_spokesgirl.mp4','wb') as f:
  73. r = requests.get(url, headers=headers, stream=True)
  74. if r.status_code == 404:
  75. return False
  76. for chunk in r.iter_content(chunk_size=1024):
  77. if chunk:
  78. f.write(chunk)
  79. return True
  80. def text_to_short_vedio_create(read_csv_use="導盲犬協會.csv",pwd_use="導盲犬協會影片素材2/",op="input_self/LOGO_OP_4.mp4",ed="input_self/LOGO_ED.mp4",bg="input_self/bg_finall_long.mp4",input_zip="input/input_data.zip"):
  81. t = openshot.Timeline(1280, 720, openshot.Fraction(30000, 1000), 44100, 2, openshot.LAYOUT_STEREO)
  82. t.Open()
  83. files=os.listdir("input/")
  84. print(files)
  85. for i in files:
  86. if i[-4:]==".zip":
  87. input_zip="input/"+i
  88. break
  89. # try:
  90. # os.remove("./templates/index.html")
  91. # except:
  92. # pass
  93. with zipfile.ZipFile(input_zip, 'r') as zf:
  94. for fn in zf.namelist():
  95. right_fn = fn.encode('cp437').decode('big5') # 將檔名正確編碼
  96. check_p=right_fn.split("/")
  97. if right_fn[-1]=="/" :
  98. os.mkdir("input/"+right_fn[:-1])
  99. pwd_use=right_fn
  100. break
  101. if len(check_p)==2:
  102. os.mkdir("input/"+check_p[0])
  103. pwd_use=check_p[0]+"/"
  104. break
  105. with zipfile.ZipFile(input_zip, 'r') as zf:
  106. for fn in zf.namelist():
  107. right_fn = fn.encode('cp437').decode('big5') # 將檔名正確編碼
  108. check_p=right_fn.split("/")
  109. if right_fn[-1]=="/" :
  110. continue
  111. if right_fn[-4:]==".csv":
  112. read_csv_use = right_fn
  113. with open("input/"+right_fn, 'wb') as output_file: # 建立並開啟新檔案
  114. with zf.open(fn, 'r') as origin_file: # 開啟原檔案
  115. shutil.copyfileobj(origin_file, output_file) # 將原檔案內容複製到新檔案
  116. # 去背參數
  117. # ck=cKey(0,254,0,270)
  118. ck=cKey(0,255,0,320)
  119. ck_anchor=None
  120. #時間
  121. time_= 0
  122. csv_use=pd.read_csv("input/"+read_csv_use)
  123. csv_use=csv_use.dropna(how='all')
  124. csv_use.reset_index(inplace=True)
  125. t1 = openshot.Timeline(1280, 720, openshot.Fraction(30000, 1000), 44100, 2, openshot.LAYOUT_STEREO)
  126. t1.Open()
  127. frames = 0
  128. for i in range(len(csv_use)):
  129. # for i in range(3):
  130. pwd_=str(csv_use.loc[i,['音檔']].values[0])
  131. locals()['anchor_music_t'+str(i)] = openshot.FFmpegReader("input/"+pwd_use+pwd_)
  132. locals()['anchor_music_t'+str(i)].Open()
  133. locals()['anchor_music_t'+str(i)+'clip'] = video_photo_clip(video=locals()['anchor_music_t'+str(i)],layer=3,scale_x=0,scale_y=0,
  134. location_x=0,location_y=0,position=time_, end=locals()['anchor_music_t'+str(i)].info.duration,ck=ck_anchor,audio=True)
  135. t1.AddClip(locals()['anchor_music_t'+str(i)+'clip'])
  136. locals()['anchor_music_t'+str(i)].Close()
  137. time_+=locals()['anchor_music_t'+str(i)].info.duration
  138. w = video_writer_init("input_self/AI_girl/%s.mp4"%('anchor_music_t'))
  139. w.Open()
  140. frames = int(t1.info.fps)*int(time_)
  141. for n in range(frames):
  142. f=t1.GetFrame(n)
  143. w.WriteFrame(f)
  144. w.Close()
  145. t1.Close()
  146. fname=mp3_to_anchor("input_self/AI_girl/%s.mp4"%('anchor_music_t'))
  147. # print(fname)
  148. time.sleep(300)
  149. while True:
  150. result = download_mp4(fname)
  151. if result:
  152. break
  153. print('等待...')
  154. time.sleep(60)
  155. ck=cKey(0,255,0,320)
  156. ck_anchor=None
  157. #時間
  158. time_= 0
  159. csv_use=pd.read_csv("input/"+read_csv_use)
  160. csv_use=csv_use.dropna(how='all')
  161. csv_use.reset_index(inplace=True)
  162. anchor_op = openshot.FFmpegReader(op)
  163. anchor_op.Open()
  164. anchor_clip_op = video_photo_clip(video=anchor_op,layer=2,scale_x=1,scale_y=1,
  165. location_x=0,location_y=0,position=time_, end=anchor_op.info.duration,ck=ck_anchor,audio=True)
  166. t.AddClip(anchor_clip_op)
  167. anchor_op.Close()
  168. time_+=anchor_op.info.duration
  169. start_time = time_
  170. locals()['anchor_ad'] = openshot.FFmpegReader('input_self/AI_girl/ai_spokesgirl.mp4')
  171. locals()['anchor_ad'].Open()
  172. locals()['anchor_clip_ad'] = video_photo_clip(video=locals()['anchor_ad'],layer=6,scale_x=0.8,scale_y=0.8,
  173. location_x=0.38,location_y=0.35,position=time_, end=locals()['anchor_ad'].info.duration,ck=ck,audio=False)
  174. t.AddClip(locals()['anchor_clip_ad'])
  175. locals()['anchor_ad'].Close()
  176. for i in range(len(csv_use)):
  177. # for i in range(3):
  178. pwd_=str(csv_use.loc[i,['音檔']].values[0])
  179. layer_choose = 4
  180. locals()['anchor_music'+str(i)] = openshot.FFmpegReader("input/"+pwd_use+pwd_)
  181. locals()['anchor_music'+str(i)].Open()
  182. locals()['anchor_music'+str(i)+'clip'] = video_photo_clip(video=locals()['anchor_music'+str(i)],layer=3,scale_x=0,scale_y=0,
  183. location_x=0,location_y=0,position=time_, end=locals()['anchor_music'+str(i)].info.duration,ck=ck_anchor,audio=True)
  184. t.AddClip(locals()['anchor_music'+str(i)+'clip'])
  185. locals()['anchor_music'+str(i)].Close()
  186. if str(csv_use.loc[i,['是否要場景']].values[0])=="是":
  187. scale_x_use = 0.59
  188. scale_y_use = 0.59
  189. else:
  190. scale_x_use = 1
  191. scale_y_use = 1
  192. layer_choose = 7
  193. choose=str(csv_use.loc[i,['素材']].values[0]).split(".")[-1]
  194. pwd_p1=str(csv_use.loc[i,['素材']].values[0])
  195. if choose == 'mp4':
  196. locals()['anchor'+str(i)] = openshot.FFmpegReader("input/"+pwd_use+pwd_p1)
  197. locals()['anchor'+str(i)].Open()
  198. locals()['anchor'+str(i)+'clip'] = video_photo_clip(video=locals()['anchor'+str(i)],layer=layer_choose,scale_x=scale_x_use,scale_y=scale_y_use,
  199. location_x=-0.04,location_y=-0.04,position=time_, end=locals()['anchor_music'+str(i)].info.duration,ck=ck_anchor,audio=False)
  200. t.AddClip(locals()['anchor'+str(i)+'clip'])
  201. locals()['anchor'+str(i)].Close()
  202. # if str(csv_use.loc[i,['是否要場景']].values[0])=="是":
  203. # locals()['anchor_bg'+str(i)] = openshot.FFmpegReader(bg)
  204. # locals()['anchor_bg'+str(i)].Open()
  205. # locals()['anchor_clip_bg'+str(i)] = video_photo_clip(video=locals()['anchor_bg'+str(i)],layer=2,scale_x=1,scale_y=1,
  206. # location_x=0,location_y=0,position=time_, end=locals()['anchor_music'+str(i)].info.duration,ck=ck,audio=False)
  207. # t.AddClip(locals()['anchor_clip_bg'+str(i)])
  208. # locals()['anchor_bg'+str(i)].Close()
  209. elif choose == 'jpg' or choose == 'png':
  210. locals()['anchor'+str(i)] = openshot.QtImageReader("input/"+pwd_use+pwd_p1)
  211. locals()['anchor'+str(i)].Open()
  212. locals()['anchor'+str(i)+'clip'] = video_photo_clip(video=locals()['anchor'+str(i)],layer=layer_choose,scale_x=scale_x_use,scale_y=scale_y_use,
  213. location_x=-0.04,location_y=-0.04,position=time_, end=locals()['anchor_music'+str(i)].info.duration,ck=ck_anchor,audio=False)
  214. t.AddClip(locals()['anchor'+str(i)+'clip'])
  215. locals()['anchor'+str(i)].Close()
  216. # if str(csv_use.loc[i,['是否要場景']].values[0])=="是":
  217. # locals()['anchor_bg'+str(i)] = openshot.FFmpegReader(bg)
  218. # locals()['anchor_bg'+str(i)].Open()
  219. # locals()['anchor_clip_bg'+str(i)] = video_photo_clip(video=locals()['anchor_bg'+str(i)],layer=2,scale_x=1,scale_y=1,
  220. # location_x=0,location_y=0,position=time_, end=locals()['anchor_music'+str(i)].info.duration,ck=ck,audio=False)
  221. # t.AddClip(locals()['anchor_clip_bg'+str(i)])
  222. # locals()['anchor_bg'+str(i)].Close()
  223. time_+=locals()['anchor_music'+str(i)].info.duration
  224. locals()['anchor_bg'] = openshot.FFmpegReader(bg)
  225. locals()['anchor_bg'].Open()
  226. locals()['anchor_clip_bg'] = video_photo_clip(video=locals()['anchor_bg'],layer=2,scale_x=1,scale_y=1,
  227. location_x=0,location_y=0,position=start_time, end=time_-start_time,ck=ck,audio=False)
  228. t.AddClip(locals()['anchor_clip_bg'])
  229. locals()['anchor_bg'].Close()
  230. anchor_ed = openshot.FFmpegReader(ed)
  231. anchor_ed.Open()
  232. anchor_clip_ed = video_photo_clip(video=anchor_ed,layer=2,scale_x=1,scale_y=1,
  233. location_x=0,location_y=0,position=time_, end=anchor_ed.info.duration,ck=ck_anchor,audio=True)
  234. time_+=anchor_ed.info.duration
  235. t.AddClip(anchor_clip_ed)
  236. anchor_ed.Close()
  237. w = video_writer_init("input_self/tmp1/test.mp4")
  238. w.Open()
  239. frames = int(t.info.fps)*int(time_)
  240. for n in range(frames):
  241. f=t.GetFrame(n)
  242. w.WriteFrame(f)
  243. t.Close()
  244. w.Close()
  245. shutil.rmtree('input')
  246. os.mkdir('input')
  247. shutil.rmtree('input_self/AI_girl')
  248. os.mkdir('input_self/AI_girl')
  249. #文字轉圖片
  250. def txt2image(content, save_target,lang='zh',size=26,fon="input_self/font/DFT_B7.ttc"):
  251. unicode_text = trim_punctuation(content)
  252. font = ''
  253. if lang=='zh':
  254. font = ImageFont.truetype(font=fon, size=size)
  255. else :
  256. font = ImageFont.truetype(font="input_self/font/arial.ttf", size=size)
  257. W, H = (1280,500)
  258. canvas = Image.new('RGB', (W, H), "#00FF00")
  259. draw = ImageDraw.Draw(canvas)
  260. text= content
  261. if "\n" in text:
  262. w, h = draw.textsize(text.split("\n")[0],font = font)
  263. #draw.text(((W-w)/2,0), text[0:18],'black', font)
  264. text_border(draw,(W-w)/2,0,text.split("\n")[0],font,'black','white')
  265. w, h = draw.textsize(text.split("\n")[1],font = font)
  266. #draw.text(((W-w)/2,h+2), text[18:],'black', font)
  267. text_border(draw,(W-w)/2,h+2,text.split("\n")[1],font,'black','white')
  268. else:
  269. w, h = draw.textsize(content,font = font)
  270. #draw.text(((W-w)/2,0), text,'black', font)
  271. text_border(draw,(W-w)/2,0,text,font,'black','white')
  272. canvas.save(save_target, "PNG")
  273. def text_border(draw,x,y,text,font,shadowcolor,fillcolor):
  274. draw.text((x-1, y), text, font=font, fill=shadowcolor)
  275. draw.text((x+1, y), text, font=font, fill=shadowcolor)
  276. draw.text((x, y-1), text, font=font, fill=shadowcolor)
  277. draw.text((x, y+1), text, font=font, fill=shadowcolor)
  278. draw.text((x-1, y+1), text, font=font, fill=shadowcolor)
  279. draw.text((x+1, y-1), text, font=font, fill=shadowcolor)
  280. draw.text((x-1, y-1), text, font=font, fill=shadowcolor)
  281. draw.text((x+1, y+1), text, font=font, fill=shadowcolor)
  282. # thicker border
  283. draw.text((x-2, y-2), text, font=font, fill=shadowcolor)
  284. draw.text((x+2, y-2), text, font=font, fill=shadowcolor)
  285. draw.text((x-2, y+2), text, font=font, fill=shadowcolor)
  286. draw.text((x+2, y+2), text, font=font, fill=shadowcolor)
  287. # now draw the text over it
  288. draw.text((x, y), text, font=font, fill=fillcolor)
  289. def srt_to_csv(srt_file):
  290. subs = pysrt.open(srt_file)
  291. csv_file = srt_file.split('.')[0] + ".csv"
  292. with open(csv_file, 'w', newline='') as csvfile:
  293. # 建立 CSV 檔寫入器
  294. writer = csv.writer(csvfile)
  295. for context in subs:
  296. writer.writerow([context.index, context.start,context.end, context.text])
  297. return csv_file
  298. def csv_to_text(csv_file,text_font):
  299. text_form = []
  300. with open(csv_file, newline='') as csvfile:
  301. # 讀取 CSV 檔案內容
  302. rows = csv.reader(csvfile)
  303. # 以迴圈輸出每一列
  304. for row in rows:
  305. start = datetime.strptime(row[1], "%H:%M:%S,%f")
  306. end = datetime.strptime(row[2], "%H:%M:%S,%f") - datetime.strptime(row[1], "%H:%M:%S,%f")
  307. end_timeStamp=end.seconds+0.000001*end.microseconds
  308. start_timeStamp=start.minute*60+start.second+ 0.000001*start.microsecond
  309. text_form.append({'text':row[3],'start':start_timeStamp,'end':end_timeStamp,'size':36,'font':text_font})
  310. return text_form
  311. def text_to_short_vedio(mp4_file ,sound_file,output_filename,text_font):
  312. t = openshot.Timeline(1280, 720, openshot.Fraction(30000, 1000), 44100, 2, openshot.LAYOUT_STEREO)
  313. t.Open()
  314. # 去背參數
  315. ck = cKey(0, 254, 0, 270)
  316. ck_anchor = cKey(0, 255, 0, 320)
  317. anchor = openshot.FFmpegReader(mp4_file)
  318. anchor.Open()
  319. anchor_clip = video_photo_clip(video=anchor,layer=2,scale_x=1,scale_y=1,
  320. location_x=0,location_y=0,position=0, end=anchor.info.duration,audio=True)
  321. t.AddClip(anchor_clip)
  322. anchor.Close()
  323. number = 0
  324. sound_srt_file = ""
  325. #音檔自動產生srt(逐字稿)
  326. if ".srt" in sound_file:
  327. sound_srt_file = sound_file
  328. elif not sound_file is None:
  329. cmd = "autosub -S zh-TW -D zh-TW " + sound_file
  330. os.system(cmd)
  331. sound_srt_file = sound_file.split('.')[0] + ".srt"
  332. csv_file = srt_to_csv(sound_srt_file)
  333. text_form = csv_to_text(csv_file,text_font)
  334. print(sound_srt_file)
  335. #開啟srt檔
  336. try:
  337. # subs = pysrt.open(sound_srt_file)
  338. # text_form = []
  339. # for context in subs:
  340. # #print(context.start.minutes*60+context.start.seconds+ 0.001*context.start.milliseconds)
  341. # end = context.end-context.start
  342. # end_timeStamp=(end.minutes*60+end.seconds+ 0.001*end.milliseconds)
  343. # start_timeStamp=(context.start.minutes*60+context.start.seconds+ 0.001*context.start.milliseconds)
  344. # text_form.append({'text':context.text,'start':start_timeStamp,'end':end_timeStamp,'size':36,'font':text_font})
  345. number = 0
  346. for text_tmp in text_form:
  347. file_name = "input_self/tmp/save_target_" + str(number) + ".png"
  348. txt2image(text_tmp['text'], file_name,lang='zh',size = text_tmp['size'],fon = text_tmp['font'])
  349. exec('text_anchor_{} = openshot.QtImageReader("input_self/tmp/save_target_{}.png")'.format(number,number))
  350. exec('text_anchor_{}.Open()'.format(number))
  351. exec('text_anchor_{}.Open()'.format(number))
  352. exec('text_anchor_clip_{} = video_photo_clip(video=text_anchor_{},layer=4,scale_x=1,scale_y=1,\
  353. location_x=0,location_y=0.67,position=text_tmp["start"], end=text_tmp["end"],ck=ck_anchor,audio=True)'.format(number,number))
  354. exec('t.AddClip(text_anchor_clip_{})'.format(number))
  355. exec('text_anchor_{}.Close()'.format(number))
  356. number = number+1
  357. except:
  358. print("無法開啟srt檔案(字幕產生失敗)")
  359. w = video_writer_init(output_filename)
  360. w.Open()
  361. frames = int(t.info.fps)*int(anchor.info.duration)
  362. for n in range(frames):
  363. f=t.GetFrame(n)
  364. w.WriteFrame(f)
  365. t.Close()
  366. w.Close()
  367. #刪除暫存檔案
  368. shutil.rmtree('input_self/tmp')
  369. os.mkdir('input_self/tmp')
  370. shutil.rmtree('input_self/tmp1')
  371. os.mkdir('input_self/tmp1')
  372. if __name__ == '__main__':
  373. # text_to_short_vedio_create()
  374. text_to_short_vedio(mp4_file = "input_self/tmp1/test.mp4",sound_file ='input_self/tmp1/test.mp4',
  375. output_filename="output/demo.mp4",text_font ="input_self/font/DFT_R7.ttc")