import openshot import re from PIL import Image,ImageDraw,ImageFont import pandas as pd import os import cv2 import numpy as np # import moviepy.editor as mp import time import pysrt import shutil import rpyc import random import string import requests from bs4 import BeautifulSoup import zipfile import csv from datetime import datetime import rarfile import gspread from oauth2client.service_account import ServiceAccountCredentials import fire def cKey(r,g,b,fuzz): col=openshot.Color() col.red=openshot.Keyframe(r) col.green=openshot.Keyframe(g) col.blue=openshot.Keyframe(b) return openshot.ChromaKey(col, openshot.Keyframe(fuzz)) def video_writer_init(path): w = openshot.FFmpegWriter(path) w.SetAudioOptions(True, "aac", 44100, 2, openshot.LAYOUT_STEREO, 3000000) w.SetVideoOptions(True, "libx264", openshot.Fraction(30000, 1000), 1280, 720, openshot.Fraction(1, 1), False, False, 3000000) return w def video_photo_clip(video=None,layer=None, position=None, end=None ,scale_x=1,scale_y=1,location_x=0,location_y=0,ck=None,audio=True): clip = openshot.Clip(video) clip.Layer(layer) clip.Position(position) clip.End(end) clip.scale_x=openshot.Keyframe(scale_x) clip.scale_y=openshot.Keyframe(scale_y) clip.location_x=openshot.Keyframe(location_x) clip.location_y=openshot.Keyframe(location_y) if ck!=None: clip.AddEffect(ck) if audio==True: clip.has_audio=openshot.Keyframe(1) else: clip.has_audio=openshot.Keyframe(0) return clip def trim_punctuation(s): pat_block = u'[^\u4e00-\u9fff0-9a-zA-Z]+' pattern = u'([0-9]+{0}[0-9]+)|{0}'.format(pat_block) res = re.sub(pattern, lambda x: x.group(1) if x.group(1) else u" " ,s) return res def randomString(stringLength=10): letters = string.ascii_lowercase return ''.join(random.choice(letters) for i in range(stringLength)) def mp3_to_anchor(fname): conn = rpyc.classic.connect("192.168.192.221",18812) fr=open(fname,'rb') ropen = conn.builtins.open randname=randomString(10) finalname=randomString(10) fw=ropen('/tmp/'+randname+'.mp4','wb') fw.write(fr.read()) fw.close() ros = conn.modules.os ros.system('/root/to_video/p9.sh '+randname+".mp4 "+finalname+".mp4") return 'http://192.168.192.221/video/'+finalname+'.mp4' # conn.execute('import os') def download_mp4(url): 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'} with open('input_self/AI_girl/ai_spokesgirl.mp4','wb') as f: r = requests.get(url, headers=headers, stream=True) if r.status_code == 404: return False for chunk in r.iter_content(chunk_size=1024): if chunk: f.write(chunk) return True def text_to_short_vedio_create(op="input_self/LOGO_OP_4.mp4",ed="input_self/LOGO_ED.mp4",bg="input_self/xchoozmo_long.mp4",input_zip="input/input_data.zip",save_type="excel"): files=os.listdir("input/") print(files) if len(files)==0: print("input資料夾沒有檔案,請放入") return elif len(files)>1: print("檔案只能有一個,且為壓縮檔") return if files[0][-4:]==".zip": input_zip="input/"+files[0] with zipfile.ZipFile(input_zip, 'r') as zf: for fn in zf.namelist(): right_fn = fn.encode('cp437').decode('big5') # 將檔名正確編碼 check_p=right_fn.split("/") if right_fn[-1]=="/" : os.mkdir("input/"+right_fn[:-1]) pwd_use=right_fn break if len(check_p)==2: os.mkdir("input/"+check_p[0]) pwd_use=check_p[0]+"/" break read_csv_use = 0 with zipfile.ZipFile(input_zip, 'r') as zf: for fn in zf.namelist(): right_fn = fn.encode('cp437').decode('big5') # 將檔名正確編碼 check_p=right_fn.split("/") if right_fn[-1]=="/" : continue if right_fn[-4:]==".csv" or right_fn[-5:]==".xlsx": read_csv_use = right_fn with open("input/"+right_fn, 'wb') as output_file: # 建立並開啟新檔案 with zf.open(fn, 'r') as origin_file: # 開啟原檔案 shutil.copyfileobj(origin_file, output_file) # 將原檔案內容複製到新檔案 elif files[0][-4:]==".rar": rar_ = rarfile.RarFile("input/"+files[0]) names = rar_.namelist() for name in names: if name[-4:]==".csv" or name[-5:]==".xlsx": read_csv_use = name pwd_use=name.split("/")[0]+"/" rar_.extract(name,"input/") rar_.close() else: print("目前不支援此壓縮格式或不為壓縮檔") return # 去背參數 # ck=cKey(0,254,0,270) ck=cKey(0,255,0,320) ck_anchor=None #時間 time_= 0 try: if read_csv_use[-4:]==".csv": csv_use=pd.read_csv("input/"+read_csv_use) elif read_csv_use[-5:]==".xlsx": csv_use = pd.read_excel("input/"+read_csv_use) except: print("excel不存在或excel格式不支援或損毀") return csv_use=csv_use.dropna(how='all') csv_use.reset_index(inplace=True) # print(csv_use) t1 = openshot.Timeline(1280, 720, openshot.Fraction(30000, 1000), 44100, 2, openshot.LAYOUT_STEREO) t1.Open() frames = 0 for i in range(len(csv_use)): # for i in range(3): pwd_=str(csv_use.loc[i,['音檔']].values[0]) locals()['anchor_music_t'+str(i)] = openshot.FFmpegReader("input/"+pwd_use+pwd_) locals()['anchor_music_t'+str(i)].Open() 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, location_x=0,location_y=0,position=time_, end=locals()['anchor_music_t'+str(i)].info.duration,ck=ck_anchor,audio=True) t1.AddClip(locals()['anchor_music_t'+str(i)+'clip']) locals()['anchor_music_t'+str(i)].Close() time_+=locals()['anchor_music_t'+str(i)].info.duration w = video_writer_init("input_self/AI_girl/%s.mp4"%('anchor_music_t')) w.Open() frames = int(t1.info.fps)*int(time_) for n in range(frames): f=t1.GetFrame(n) w.WriteFrame(f) w.Close() t1.Close() fname=mp3_to_anchor("input_self/AI_girl/%s.mp4"%('anchor_music_t')) # print(fname) time.sleep(300) while True: result = download_mp4(fname) if result: break print('等待...') time.sleep(60) t = openshot.Timeline(1280, 720, openshot.Fraction(30000, 1000), 44100, 2, openshot.LAYOUT_STEREO) t.Open() ck=cKey(0,255,0,320) ck_anchor=None #時間 time_= 0 try: if read_csv_use[-4:]==".csv": csv_use=pd.read_csv("input/"+read_csv_use) elif read_csv_use[-5:]==".xlsx": csv_use = pd.read_excel("input/"+read_csv_use) except: print("excel不存在或excel格式不支援或損毀") return csv_use=csv_use.dropna(how='all') csv_use.reset_index(inplace=True) anchor_op = openshot.FFmpegReader(op) anchor_op.Open() anchor_clip_op = video_photo_clip(video=anchor_op,layer=2,scale_x=1,scale_y=1, location_x=0,location_y=0,position=time_, end=anchor_op.info.duration,ck=ck_anchor,audio=True) t.AddClip(anchor_clip_op) anchor_op.Close() time_+=anchor_op.info.duration start_time = time_ for i in range(len(csv_use)): # for i in range(3): pwd_=str(csv_use.loc[i,['音檔']].values[0]) layer_choose = 4 location_x_choose=-0.04 location_y_choose=-0.04 locals()['anchor_music'+str(i)] = openshot.FFmpegReader("input/"+pwd_use+pwd_) locals()['anchor_music'+str(i)].Open() locals()['anchor_music'+str(i)+'clip'] = video_photo_clip(video=locals()['anchor_music'+str(i)],layer=3,scale_x=0,scale_y=0, location_x=0,location_y=0,position=time_, end=locals()['anchor_music'+str(i)].info.duration,ck=ck_anchor,audio=True) t.AddClip(locals()['anchor_music'+str(i)+'clip']) locals()['anchor_music'+str(i)].Close() if str(csv_use.loc[i,['是否要場景']].values[0])=="是": # scale_x_use = 0.59 # scale_y_use = 0.59 scale_x_use = 0.8 scale_y_use = 0.82 location_x_choose=0 location_y_choose=-0.039 else: scale_x_use = 1 scale_y_use = 1 layer_choose = 8 location_x_choose=0 location_y_choose=0 locals()['anchor_black'+str(i)] = openshot.QtImageReader("input_self/black.jpg") locals()['anchor_black'+str(i)].Open() locals()['anchor_black'+str(i)+'clip'] = video_photo_clip(video=locals()['anchor_black'+str(i)],layer=7,scale_x=scale_x_use,scale_y=scale_y_use, location_x=0,location_y=0,position=time_, end=locals()['anchor_music'+str(i)].info.duration,ck=ck_anchor,audio=False) t.AddClip(locals()['anchor_black'+str(i)+'clip']) locals()['anchor_black'+str(i)].Close() choose=str(csv_use.loc[i,['素材']].values[0]).split(".")[-1] pwd_p1=str(csv_use.loc[i,['素材']].values[0]) if choose == 'mp4': locals()['anchor'+str(i)] = openshot.FFmpegReader("input/"+pwd_use+pwd_p1) locals()['anchor'+str(i)].Open() 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, location_x=location_x_choose,location_y=location_y_choose,position=time_, end=locals()['anchor_music'+str(i)].info.duration,ck=ck_anchor,audio=False) t.AddClip(locals()['anchor'+str(i)+'clip']) locals()['anchor'+str(i)].Close() elif choose == 'jpg' or choose == 'png': locals()['anchor'+str(i)] = openshot.QtImageReader("input/"+pwd_use+pwd_p1) locals()['anchor'+str(i)].Open() 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, location_x=location_x_choose,location_y=location_y_choose,position=time_, end=locals()['anchor_music'+str(i)].info.duration,ck=ck_anchor,audio=False) t.AddClip(locals()['anchor'+str(i)+'clip']) locals()['anchor'+str(i)].Close() time_+=locals()['anchor_music'+str(i)].info.duration locals()['anchor_bg'] = openshot.FFmpegReader(bg) locals()['anchor_bg'].Open() locals()['anchor_clip_bg'] = video_photo_clip(video=locals()['anchor_bg'],layer=5,scale_x=1,scale_y=1, location_x=0,location_y=0,position=start_time, end=time_-start_time,ck=ck,audio=False) t.AddClip(locals()['anchor_clip_bg']) locals()['anchor_bg'].Close() # locals()['anchor_ad'] = openshot.FFmpegReader('input_self/puuet主播_long.mp4') locals()['anchor_ad'] = openshot.FFmpegReader('input_self/AI_girl/ai_spokesgirl.mp4') locals()['anchor_ad'].Open() locals()['anchor_clip_ad'] = video_photo_clip(video=locals()['anchor_ad'],layer=6,scale_x=0.8,scale_y=0.8, location_x=0.38,location_y=0.35,position=start_time , end=time_-start_time,ck=ck,audio=False) t.AddClip(locals()['anchor_clip_ad']) locals()['anchor_ad'].Close() anchor_ed = openshot.FFmpegReader(ed) anchor_ed.Open() anchor_clip_ed = video_photo_clip(video=anchor_ed,layer=2,scale_x=1,scale_y=1, location_x=0,location_y=0,position=time_, end=anchor_ed.info.duration,ck=ck_anchor,audio=True) time_+=anchor_ed.info.duration t.AddClip(anchor_clip_ed) anchor_ed.Close() w = video_writer_init("output/no_captions/test.mp4") w.Open() frames = int(t.info.fps)*int(time_) for n in range(frames): f=t.GetFrame(n) w.WriteFrame(f) t.Close() w.Close() shutil.rmtree('input') os.mkdir('input') shutil.rmtree('input_self/AI_girl') os.mkdir('input_self/AI_girl') if save_type=="excel": vedio_to_csv() elif save_type=="sheet": vedio_to_sheet() return True #文字轉圖片 def txt2image(content, save_target,lang='zh',size=26,fon="input_self/font/DFT_B7.ttc"): unicode_text = trim_punctuation(content) font = '' if lang=='zh': font = ImageFont.truetype(font=fon, size=size) else : font = ImageFont.truetype(font="input_self/font/arial.ttf", size=size) W, H = (1280,500) canvas = Image.new('RGB', (W, H), "#00FF00") draw = ImageDraw.Draw(canvas) text= content if "\n" in text: w, h = draw.textsize(text.split("\n")[0],font = font) #draw.text(((W-w)/2,0), text[0:18],'black', font) text_border(draw,(W-w)/2,0,text.split("\n")[0],font,'black','white') w, h = draw.textsize(text.split("\n")[1],font = font) #draw.text(((W-w)/2,h+2), text[18:],'black', font) text_border(draw,(W-w)/2,h+2,text.split("\n")[1],font,'black','white') else: w, h = draw.textsize(content,font = font) #draw.text(((W-w)/2,0), text,'black', font) text_border(draw,(W-w)/2,0,text,font,'black','white') canvas.save(save_target, "PNG") def text_border(draw,x,y,text,font,shadowcolor,fillcolor): draw.text((x-1, y), text, font=font, fill=shadowcolor) draw.text((x+1, y), text, font=font, fill=shadowcolor) draw.text((x, y-1), text, font=font, fill=shadowcolor) draw.text((x, y+1), text, font=font, fill=shadowcolor) draw.text((x-1, y+1), text, font=font, fill=shadowcolor) draw.text((x+1, y-1), text, font=font, fill=shadowcolor) draw.text((x-1, y-1), text, font=font, fill=shadowcolor) draw.text((x+1, y+1), text, font=font, fill=shadowcolor) # thicker border draw.text((x-2, y-2), text, font=font, fill=shadowcolor) draw.text((x+2, y-2), text, font=font, fill=shadowcolor) draw.text((x-2, y+2), text, font=font, fill=shadowcolor) draw.text((x+2, y+2), text, font=font, fill=shadowcolor) # now draw the text over it draw.text((x, y), text, font=font, fill=fillcolor) def srt_to_csv(srt_file,sound_file="output/csv_produce/test.csv"): subs = pysrt.open(srt_file) # csv_file = srt_file.split('.')[0] + ".csv" with open(sound_file, 'w', newline='',encoding="big5") as csvfile: # 建立 CSV 檔寫入器 writer = csv.writer(csvfile) writer.writerow(["序號","開始時間","結束時間","字幕內容"]) for context in subs: writer.writerow([context.index, context.start,context.end, context.text]) return def csv_to_text(csv_file,text_font): text_form = [] with open(csv_file, newline='',encoding="big5") as csvfile: # 讀取 CSV 檔案內容 rows = csv.reader(csvfile) # 以迴圈輸出每一列 for row in rows: start = datetime.strptime(row[1], "%H:%M:%S,%f") end = datetime.strptime(row[2], "%H:%M:%S,%f") - datetime.strptime(row[1], "%H:%M:%S,%f") end_timeStamp=end.seconds+0.000001*end.microseconds start_timeStamp=start.minute*60+start.second+ 0.000001*start.microsecond text_form.append({'text':row[3],'start':start_timeStamp,'end':end_timeStamp,'size':36,'font':text_font}) return text_form def text_to_short_vedio(mp4_file ,sound_file,output_filename,text_font): t = openshot.Timeline(1280, 720, openshot.Fraction(30000, 1000), 44100, 2, openshot.LAYOUT_STEREO) t.Open() # 去背參數 ck = cKey(0, 254, 0, 270) ck_anchor = cKey(0, 255, 0, 320) anchor = openshot.FFmpegReader(mp4_file) anchor.Open() anchor_clip = video_photo_clip(video=anchor,layer=2,scale_x=1,scale_y=1, location_x=0,location_y=0,position=0, end=anchor.info.duration,audio=True) t.AddClip(anchor_clip) anchor.Close() number = 0 sound_srt_file = "" #音檔自動產生srt(逐字稿) if ".srt" in sound_file: sound_srt_file = sound_file elif not sound_file is None: cmd = "autosub -S zh-TW -D zh-TW " + sound_file os.system(cmd) sound_srt_file = sound_file.split('.')[0] + ".srt" csv_file = srt_to_csv(sound_srt_file) text_form = csv_to_text(csv_file,text_font) print(sound_srt_file) #開啟srt檔 try: number = 0 for text_tmp in text_form: file_name = "input_self/tmp/save_target_" + str(number) + ".png" txt2image(text_tmp['text'], file_name,lang='zh',size = text_tmp['size'],fon = text_tmp['font']) exec('text_anchor_{} = openshot.QtImageReader("input_self/tmp/save_target_{}.png")'.format(number,number)) exec('text_anchor_{}.Open()'.format(number)) exec('text_anchor_{}.Open()'.format(number)) exec('text_anchor_clip_{} = video_photo_clip(video=text_anchor_{},layer=4,scale_x=1,scale_y=1,\ location_x=0,location_y=0.78,position=text_tmp["start"], end=text_tmp["end"],ck=ck_anchor,audio=True)'.format(number,number)) exec('t.AddClip(text_anchor_clip_{})'.format(number)) exec('text_anchor_{}.Close()'.format(number)) number = number+1 except: print("無法開啟srt檔案(字幕產生失敗)") w = video_writer_init(output_filename) w.Open() frames = int(t.info.fps)*int(anchor.info.duration) for n in range(frames): f=t.GetFrame(n) w.WriteFrame(f) t.Close() w.Close() # 刪除暫存檔案 # shutil.rmtree('input_self/tmp') # os.mkdir('input_self/tmp') # shutil.rmtree('input_self/tmp1') # os.mkdir('input_self/tmp1') def auth_gss_client(path, scopes): credentials = ServiceAccountCredentials.from_json_keyfile_name(path, scopes) return gspread.authorize(credentials) auth_json_path = 'noted-tesla-348011-74f70c9caeda.json' #由剛剛建立出的憑證,放置相同目錄以供引入 gss_scopes = ['https://spreadsheets.google.com/feeds'] #我們想要取用的範圍 gss_client = auth_gss_client(auth_json_path, gss_scopes) #呼叫我們的函式 #從剛剛建立的sheet,把網址中 https://docs.google.com/spreadsheets/d/〔key〕/edit 的 〔key〕的值代入 spreadsheet_key_path = '1LU5O8-oAotIFGPI9STPbElO0NHGA6eynuv9sYz81aOw' wks = gss_client.open_by_key(spreadsheet_key_path).sheet1 def srt_to_sheet(srt_file): subs = pysrt.open(srt_file) wks.clear() index = 0 for context in subs: index = context.index cell_list = wks.range('A1:C'+str(index)) number = 0 for context in subs: #print(context.start.minutes*60+context.start.seconds+ 0.001*context.start.milliseconds) index = context.index end = context.end start = context.start #print('A'+str(index)) cell_list[number].value = str(start) cell_list[number+1].value = str(end) cell_list[number+2].value = str(context.text) number = number+3 wks.update_cells(cell_list) def vedio_to_csv(sound_file = "output/no_captions/test.mp4"): cmd = "autosub -S zh-TW -D zh-TW " + sound_file os.system(cmd) sound_srt_file = sound_file.split('.')[0] + ".srt" srt_to_csv(sound_srt_file,sound_file) def vedio_to_sheet(): cmd = "autosub -S zh-TW -D zh-TW " + sound_file os.system(cmd) sound_srt_file = sound_file.split('.')[0] + ".srt" srt_to_sheet(sound_srt_file) if __name__ == '__main__': # fire.Fire() fire.Fire(text_to_short_vedio_create( op="input_self/LOGO_OP_4.mp4", ed="input_self/LOGO_ED.mp4", bg="input_self/xchoozmo_long.mp4", input_zip="input/input_data.zip", save_type="excel")) # vedio_to_csv("output/no_captions/test.mp4")