123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622 |
- 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=cKey(255,0,255,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.95
- # scale_y_use = 0.82
- scale_x_use = 0.8
- scale_y_use = 0.7
- 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':
- print("input/"+pwd_use+pwd_p1)
- 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':
- print("input/"+pwd_use+pwd_p1)
- 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)
- 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()
- # 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")
- fire.Fire(text_to_short_vedio_create)
- # 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")
-
-
|