openshot_word.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. import openshot
  2. import os
  3. import re
  4. import time
  5. import pysrt
  6. import shutil
  7. from datetime import datetime
  8. from PIL import Image,ImageDraw,ImageFont
  9. import gspread
  10. import pandas as pd
  11. import csv
  12. from oauth2client.service_account import ServiceAccountCredentials
  13. def auth_gss_client(path, scopes):
  14. credentials = ServiceAccountCredentials.from_json_keyfile_name(path, scopes)
  15. return gspread.authorize(credentials)
  16. auth_json_path = 'noted-tesla-348011-74f70c9caeda.json' #由剛剛建立出的憑證,放置相同目錄以供引入
  17. gss_scopes = ['https://spreadsheets.google.com/feeds'] #我們想要取用的範圍
  18. gss_client = auth_gss_client(auth_json_path, gss_scopes) #呼叫我們的函式
  19. #從剛剛建立的sheet,把網址中 https://docs.google.com/spreadsheets/d/〔key〕/edit 的 〔key〕的值代入
  20. spreadsheet_key_path = '1LU5O8-oAotIFGPI9STPbElO0NHGA6eynuv9sYz81aOw'
  21. wks = gss_client.open_by_key(spreadsheet_key_path).sheet1
  22. def srt_to_sheet(srt_file):
  23. subs = pysrt.open(srt_file)
  24. wks.clear()
  25. index = 0
  26. for context in subs:
  27. index = context.index
  28. cell_list = wks.range('A1:C'+str(index))
  29. number = 0
  30. for context in subs:
  31. #print(context.start.minutes*60+context.start.seconds+ 0.001*context.start.milliseconds)
  32. index = context.index
  33. end = context.end
  34. start = context.start
  35. #print('A'+str(index))
  36. cell_list[number].value = str(start)
  37. cell_list[number+1].value = str(end)
  38. cell_list[number+2].value = str(context.text)
  39. number = number+3
  40. wks.update_cells(cell_list)
  41. def sheet_to_text(text_font):
  42. ck_anchor = cKey(0, 255, 0, 320)
  43. text_form = []
  44. for context in wks.get_all_values():
  45. #print(context.start.minutes*60+context.start.seconds+ 0.001*context.start.milliseconds)
  46. start = datetime.strptime(context[0], "%H:%M:%S,%f")
  47. end = datetime.strptime(context[1], "%H:%M:%S,%f") - datetime.strptime(context[0], "%H:%M:%S,%f")
  48. end_timeStamp=end.seconds+0.000001*end.microseconds
  49. start_timeStamp=start.minute*60+start.second+ 0.000001*start.microsecond
  50. text_form.append({'text':context[2],'start':start_timeStamp,'end':end_timeStamp,'size':36,'font':text_font})
  51. #print({'text':context[2],'start':start_timeStamp,'end':end_timeStamp,'size':36,'font':text_font})
  52. def cKey(r,g,b,fuzz):
  53. col=openshot.Color()
  54. col.red=openshot.Keyframe(r)
  55. col.green=openshot.Keyframe(g)
  56. col.blue=openshot.Keyframe(b)
  57. return openshot.ChromaKey(col, openshot.Keyframe(fuzz))
  58. def video_writer_init(path):
  59. w = openshot.FFmpegWriter(path)
  60. w.SetAudioOptions(True, "aac", 44100, 2, openshot.LAYOUT_STEREO, 3000000)
  61. w.SetVideoOptions(True, "libx264", openshot.Fraction(30000, 1000), 1280, 720,
  62. openshot.Fraction(1, 1), False, False, 3000000)
  63. return w
  64. def video_photo_clip(video=None,layer=None, position=None, end=None
  65. ,scale_x=1,scale_y=1,location_x=0,location_y=0,ck=None,audio=True):
  66. clip = openshot.Clip(video)
  67. clip.Layer(layer)
  68. clip.Position(position)
  69. clip.End(end)
  70. clip.scale_x=openshot.Keyframe(scale_x)
  71. clip.scale_y=openshot.Keyframe(scale_y)
  72. clip.location_x=openshot.Keyframe(location_x)
  73. clip.location_y=openshot.Keyframe(location_y)
  74. if ck!=None:
  75. clip.AddEffect(ck)
  76. if audio==True:
  77. clip.has_audio=openshot.Keyframe(1)
  78. else:
  79. clip.has_audio=openshot.Keyframe(0)
  80. return clip
  81. def trim_punctuation(s):
  82. pat_block = u'[^\u4e00-\u9fff0-9a-zA-Z]+'
  83. pattern = u'([0-9]+{0}[0-9]+)|{0}'.format(pat_block)
  84. res = re.sub(pattern, lambda x: x.group(1) if x.group(1) else u" " ,s)
  85. return res
  86. #文字轉圖片
  87. def txt2image(content, save_target,lang='zh',size=26,fon="font/DFT_B7.ttc"):
  88. unicode_text = trim_punctuation(content)
  89. font = ''
  90. if lang=='zh':
  91. font = ImageFont.truetype(font=fon, size=size)
  92. else :
  93. font = ImageFont.truetype(font="font/arial.ttf", size=size)
  94. W, H = (1280,500)
  95. canvas = Image.new('RGB', (W, H), "#00FF00")
  96. draw = ImageDraw.Draw(canvas)
  97. text= content
  98. if "\n" in text:
  99. w, h = draw.textsize(text.split("\n")[0],font = font)
  100. #draw.text(((W-w)/2,0), text[0:18],'black', font)
  101. text_border(draw,(W-w)/2,0,text.split("\n")[0],font,'black','white')
  102. w, h = draw.textsize(text.split("\n")[1],font = font)
  103. #draw.text(((W-w)/2,h+2), text[18:],'black', font)
  104. text_border(draw,(W-w)/2,h+2,text.split("\n")[1],font,'black','white')
  105. else:
  106. w, h = draw.textsize(content,font = font)
  107. #draw.text(((W-w)/2,0), text,'black', font)
  108. text_border(draw,(W-w)/2,0,text,font,'black','white')
  109. canvas.save(save_target, "PNG")
  110. def text_border(draw,x,y,text,font,shadowcolor,fillcolor):
  111. draw.text((x-1, y), text, font=font, fill=shadowcolor)
  112. draw.text((x+1, y), text, font=font, fill=shadowcolor)
  113. draw.text((x, y-1), text, font=font, fill=shadowcolor)
  114. draw.text((x, y+1), text, font=font, fill=shadowcolor)
  115. draw.text((x-1, y+1), text, font=font, fill=shadowcolor)
  116. draw.text((x+1, y-1), text, font=font, fill=shadowcolor)
  117. draw.text((x-1, y-1), text, font=font, fill=shadowcolor)
  118. draw.text((x+1, y+1), text, font=font, fill=shadowcolor)
  119. # thicker border
  120. draw.text((x-2, y-2), text, font=font, fill=shadowcolor)
  121. draw.text((x+2, y-2), text, font=font, fill=shadowcolor)
  122. draw.text((x-2, y+2), text, font=font, fill=shadowcolor)
  123. draw.text((x+2, y+2), text, font=font, fill=shadowcolor)
  124. # now draw the text over it
  125. draw.text((x, y), text, font=font, fill=fillcolor)
  126. def srt_to_csv(srt_file):
  127. subs = pysrt.open(srt_file)
  128. csv_file = srt_file.split('.')[0] + ".csv"
  129. with open(csv_file, 'w', newline='') as csvfile:
  130. # 建立 CSV 檔寫入器
  131. writer = csv.writer(csvfile)
  132. for context in subs:
  133. writer.writerow([context.index, context.start,context.end, context.text])
  134. return csv_file
  135. def csv_to_text(csv_file,text_font):
  136. text_form = []
  137. with open(csv_file, newline='') as csvfile:
  138. # 讀取 CSV 檔案內容
  139. rows = csv.reader(csvfile)
  140. # 以迴圈輸出每一列
  141. for row in rows:
  142. start = datetime.strptime(row[1], "%H:%M:%S,%f")
  143. end = datetime.strptime(row[2], "%H:%M:%S,%f") - datetime.strptime(row[1], "%H:%M:%S,%f")
  144. end_timeStamp=end.seconds+0.000001*end.microseconds
  145. start_timeStamp=start.minute*60+start.second+ 0.000001*start.microsecond
  146. text_form.append({'text':row[3],'start':start_timeStamp,'end':end_timeStamp,'size':36,'font':text_font})
  147. return text_form
  148. def text_to_short_vedio(mp4_file = "input/example/test3.mp4",sound_file = None
  149. ,vedio_time = 30,output_filename="output/demo.mp4",text_font = "font/DFT_B7.ttc",mode = "csv",correct = False):
  150. t = openshot.Timeline(1280, 720, openshot.Fraction(30000, 1000), 44100, 2, openshot.LAYOUT_STEREO)
  151. t.Open()
  152. # 去背參數
  153. ck = cKey(0, 254, 0, 270)
  154. ck_anchor = cKey(0, 255, 0, 320)
  155. anchor = openshot.FFmpegReader(mp4_file)
  156. anchor.Open()
  157. anchor_clip = video_photo_clip(video=anchor,layer=2,scale_x=1,scale_y=1,
  158. location_x=0,location_y=0,position=0, end=vedio_time,audio=True)
  159. t.AddClip(anchor_clip)
  160. anchor.Close()
  161. number = 0
  162. sound_srt_file = ""
  163. text_form = []
  164. if sound_file==None:
  165. if mode!= "google":
  166. sound_file = mp4_file
  167. correct = False
  168. if ".csv" in sound_file :
  169. correct = True
  170. mode = "csv"
  171. #音檔自動產生srt(逐字稿)
  172. if not correct: # 未修正過的、需要產生SRT檔
  173. cmd = "autosub -S zh-TW -D zh-TW " + sound_file
  174. os.system(cmd)
  175. sound_srt_file = sound_file.split('.')[0] + ".srt"
  176. if mode == "google" :
  177. srt_to_sheet(sound_srt_file)
  178. text_form = sheet_to_text(text_font)
  179. elif mode == "csv" :
  180. csv_file = srt_to_csv(sound_srt_file)
  181. text_form = csv_to_text(csv_file,text_font)
  182. else:
  183. if ".srt" in sound_file:
  184. sound_srt_file = sound_file
  185. if mode == "google" :
  186. srt_to_sheet(sound_srt_file)
  187. text_form = sheet_to_text(text_font)
  188. elif mode == "csv" :
  189. csv_file = srt_to_csv(sound_srt_file)
  190. text_form = csv_to_text(csv_file,text_font)
  191. else:
  192. if mode == "google" :
  193. text_form = sheet_to_text(text_font)
  194. elif mode == "csv" :
  195. csv_file = sound_file
  196. text_form = csv_to_text(csv_file,text_font)
  197. #產生字幕
  198. try:
  199. number = 0
  200. for text_tmp in text_form:
  201. file_name = "tmp/save_target_" + str(number) + ".png"
  202. txt2image(text_tmp['text'], file_name,lang='zh',size = text_tmp['size'],fon = text_tmp['font'])
  203. exec('text_anchor_{} = openshot.QtImageReader("tmp/save_target_{}.png")'.format(number,number))
  204. exec('text_anchor_{}.Open()'.format(number))
  205. exec('text_anchor_{}.Open()'.format(number))
  206. exec('text_anchor_clip_{} = video_photo_clip(video=text_anchor_{},layer=4,scale_x=1,scale_y=1,\
  207. location_x=0,location_y=0.67,position=text_tmp["start"], end=text_tmp["end"],ck=ck_anchor,audio=True)'.format(number,number))
  208. exec('t.AddClip(text_anchor_clip_{})'.format(number))
  209. exec('text_anchor_{}.Close()'.format(number))
  210. number = number+1
  211. except:
  212. print("無法開啟srt檔案(字幕產生失敗)")
  213. w = video_writer_init(output_filename)
  214. w.Open()
  215. frames = int(t.info.fps)*int(vedio_time)
  216. for n in range(frames):
  217. f=t.GetFrame(n)
  218. w.WriteFrame(f)
  219. t.Close()
  220. w.Close()
  221. #刪除暫存檔案
  222. shutil.rmtree('tmp')
  223. os.mkdir('tmp')
  224. if __name__ == '__main__':
  225. #text_to_short_vedio(mp4_file = "input/example/導盲犬_投影片2.mp4",
  226. #sound_file ='input/example/導盲犬_投影片2.srt',vedio_time =110,text_font ="font/DFT_R7.ttc")
  227. file = srt_to_csv('input/example/導盲犬_投影片2.srt')
  228. text = csv_to_text(file,"font/DFT_R7.ttc")
  229. print(text)