瀏覽代碼

fix loop error

ming 3 年之前
父節點
當前提交
63682d05dc
共有 2 個文件被更改,包括 991 次插入40 次删除
  1. 990 39
      OpenshotService/test.py
  2. 1 1
      etc/Dockerfile

+ 990 - 39
OpenshotService/test.py

@@ -1,41 +1,992 @@
+from os import listdir
+from os.path import isfile, isdir, join
+import openshot
+import threading
+import zhtts
+import os 
+import urllib
+from typing import List
+import requests
+from pydantic import BaseModel
+from bs4 import BeautifulSoup
+from PIL import Image,ImageDraw,ImageFont
+import pyttsx3
+import rpyc
+import random
+import re
+import time
+import math
+import dataset
+from datetime import datetime
+from gtts import gTTS
+import ffmpy
+from difflib import SequenceMatcher
+import difflib
+from autosub import DEFAULT_CONCURRENCY
+from autosub import DEFAULT_SUBTITLE_FORMAT
+from pytranscriber.control.ctr_main import Ctr_Main
+from pytranscriber.control.ctr_autosub import Ctr_Autosub
+import multiprocessing
+from itertools import groupby
+from operator import itemgetter
+from util.parser import parser
 
-def image_clip_info(dict_in):
-        #if 'image_idx' in dic:
-    #            new_dic['image_obj'] = {'start':dic['start'],'idx':dic['image_idx']}
-    stopPoint = 0 # sec
-    time_info = []
-    img_idx = 1 #start from 1
-    added_idx = []
-    for dic in dict_in:
-        if 'image_obj' in dic :
-            if dic['image_obj']['idx'] not in added_idx:
-                added_idx.append(dic['image_obj']['idx'])
-                time_info.append({'index':img_idx,'start':dic['start']})
-                img_idx += 1
-        stopPoint = dic['start']+dic['duration']
+dir_sound = 'mp3_track/'
+dir_photo = 'photo/'
+dir_text = 'text_file/'
+dir_video = 'video_material/'
+dir_title = 'title/'
+dir_subtitle = 'subtitle/'
+dir_anchor = 'anchor_raw/'
+tmp_video_dir = 'tmp_video/'
+video_sub_folder = 'ai_anchor_video/'
+
+dir_list = [dir_sound,dir_photo,dir_text,dir_video,dir_title,dir_subtitle,dir_anchor,tmp_video_dir]
+
+
+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_photo_clip(vid=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(vid)
+    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 listener_progress(string, percent):
+    True
+
+def myunichchar(unicode_char):
+        mb_string = unicode_char.encode('big5')
+        try:
+            unicode_char = unichr(ord(mb_string[0]) << 8 | ord(mb_string[1]))
+        except NameError:
+            unicode_char = chr(mb_string[0] << 8 | mb_string[1])
+        return unicode_char
+
+def get_url_type(url):
+    print('---------------------------------------------')
+    print(url)
+    req = urllib.request.Request(url, method='HEAD', headers={'User-Agent': 'Mozilla/5.0'})
+    r = urllib.request.urlopen(req)
+    contentType = r.getheader('Content-Type')
+    print(contentType)
+    print('-------------------------------------------------')
+    return contentType
+    
+def make_dir(name_hash):
+    for direct in dir_list:
+        if not os.path.isdir(direct):
+            os.mkdir(direct)
+    try:
+        os.mkdir(dir_photo+name_hash)
+    except FileExistsError:
+        print("~~~~~~Warning~~~~~~~~~Directory " , dir_photo+name_hash ,  " already exists")
+    try:
+        os.mkdir(dir_text+name_hash)
+    except FileExistsError:
+        print("~~~~~~Warning~~~~~~~~~Directory " , dir_text+name_hash ,  " already exists")
+    try:
+        os.mkdir(dir_sound+name_hash)
+    except FileExistsError:
+        print("~~~~~~Warning~~~~~~~~~Directory " , dir_sound+name_hash ,  " already exists")
+    try:
+        os.mkdir(dir_anchor+name_hash)
+    except FileExistsError:
+        print("~~~~~~Warning~~~~~~~~~Directory " , dir_anchor+name_hash ,  " already exists")
+    try:
+        os.mkdir(dir_subtitle+name_hash)
+    except FileExistsError:
+        print("~~~~~~Warning~~~~~~~~~Directory " , dir_subtitle+name_hash ,  " already exists")
+
+def file_prepare(name, name_hash,text_content,image_urls,multiLang,lang='zh'):
+    make_dir(name_hash)
+    img_num = 1
+    for imgu in image_urls:
+        if get_url_type(imgu) =='video/mp4':
+            r=requests.get(imgu)
+            f=open(dir_photo+name_hash+"/"+str(img_num)+".mp4",'wb')
+            for chunk in r.iter_content(chunk_size=255): 
+                if chunk:
+                    f.write(chunk)
+            f.close()
+        else:
+            im = Image.open(requests.get(imgu, stream=True).raw)
+            im= im.convert("RGB")
+            im.save(dir_photo+name_hash+"/"+str(img_num)+".jpg")
+        img_num+=1
+    #save text
+    txt_idx=0
+    for txt in text_content:
+        text_file = open(dir_text+name_hash+"/"+str(txt_idx)+".txt", "w")
+        text_file.write(txt)
+        text_file.close()
+        txt_idx+=1
+    print("text file made")
+    #make mp3
+    txt_idx = 0
+    for txt in text_content:
+        if lang!='zh' or multiLang==1:
+            if lang!='zh':
+                tts = gTTS(txt)
+                tts.save(dir_sound+name_hash+"/"+str(txt_idx)+"raw.mp3")
+            else:
+                tts = gTTS(txt,lang='zh-tw')
+                tts.save(dir_sound+name_hash+"/"+str(txt_idx)+"raw.mp3")
+            #speed up 
+            ff = ffmpy.FFmpeg(inputs={dir_sound+name_hash+"/"+str(txt_idx)+"raw.mp3": None}
+                            , outputs={dir_sound+name_hash+"/"+str(txt_idx)+".mp3": ["-filter:a", "atempo=1.2"]})
+            ff.run()
+            os.remove(dir_sound+name_hash+"/"+str(txt_idx)+"raw.mp3")
+        else:
+            print('use zhtts')
+            tts = zhtts.TTS() 
+            tts.text2wav(txt,dir_sound+name_hash+"/"+str(txt_idx)+".mp3")
+        txt_idx+=1
+    print("mp3 file made")
+    #make title as image
+    txt2image_title(name, dir_title+name_hash+".png",lang)
+
+
+def file_prepare_long(name, name_hash,text_content,image_urls,multiLang,lang='zh'):
+    make_dir(name_hash)
+    img_num = 1
+    for imgu in image_urls:
+        if get_url_type(imgu) =='video/mp4':
+            r=requests.get(imgu)
+            f=open(dir_photo+name_hash+"/"+str(img_num)+".mp4",'wb')
+            for chunk in r.iter_content(chunk_size=255): 
+                if chunk:
+                    f.write(chunk)
+            f.close()
+        else:
+            im = Image.open(requests.get(imgu, stream=True).raw)
+            im= im.convert("RGB")
+            im.save(dir_photo+name_hash+"/"+str(img_num)+".jpg")
+        img_num+=1
+
+    #make mp3
+    text_parser = parser()
+    txt_idx = 0
+    for txt in text_content:
+        rep_list = text_parser.replace_list(txt)
+        for reptxt in rep_list:
+            txt = txt.replace(reptxt,'')
+        if lang!='zh' or multiLang==1:
+            if lang!='zh':
+                tts = gTTS(txt)
+                tts.save(dir_sound+name_hash+"/"+str(txt_idx)+"raw.mp3")
+            else:
+                tts = gTTS(txt,lang='zh-tw')
+                tts.save(dir_sound+name_hash+"/"+str(txt_idx)+"raw.mp3")
+            #speed up 
+            ff = ffmpy.FFmpeg(inputs={dir_sound+name_hash+"/"+str(txt_idx)+"raw.mp3": None}
+                            , outputs={dir_sound+name_hash+"/"+str(txt_idx)+".mp3": ["-filter:a", "atempo=1.2"]})
+            ff.run()
+            os.remove(dir_sound+name_hash+"/"+str(txt_idx)+"raw.mp3")
+        else:
+            print('use zhtts')
+            tts = zhtts.TTS() 
+            tts.text2wav(txt,dir_sound+name_hash+"/"+str(txt_idx)+".mp3")
+        txt_idx+=1
+    print("mp3 file made")
+    #make title as image
+    txt2image_title(name, dir_title+name_hash+".png",lang)
+
+def txt2image(content, save_target,lang='zh'):
+    unicode_text = trim_punctuation(content)
+    font = ''
+    if lang=='zh':
+        font = ImageFont.truetype(font="font/DFT_B7.ttc", size=38)
+    else :
+        font = ImageFont.truetype(font="font/arial.ttf", size=38)
+    text_width, text_height = font.getsize(unicode_text)
+    canvas = Image.new('RGBA', (700, 500), (255, 0, 0, 0) )
+    draw = ImageDraw.Draw(canvas)
+    text= unicode_text
+    draw.text((5,5), text, (255, 255, 0), font)
+    canvas.save(save_target, "PNG")
+
+def txt2image_title(content, save_target, lang='zh'):
+    unicode_text = trim_punctuation(content)
+    font = ''
+    if lang=='zh':
+        font = ImageFont.truetype(font="font/DFT_B7.ttc", size=22)
+    else :
+        font = ImageFont.truetype(font="font/arial.ttf", size=22)
+    text_width, text_height = font.getsize(unicode_text)
+    canvas = Image.new('RGBA', (510, 500), (255, 0, 0, 0) )
+    draw = ImageDraw.Draw(canvas)
+    text= unicode_text
+    draw.text((5,5), text, (17, 41, 167), font)
+    canvas.save(save_target, "PNG")
+
+def call_anchor(fileName,avatar):
+    conn = rpyc.classic.connect("192.168.1.105",18812)
+    ros = conn.modules.os 
+    rsys = conn.modules.sys 
+    fr=open(dir_sound+fileName+".mp3",'rb')# voice
+    #warning!!!    file my be replaced by other process
+    fw=conn.builtins.open('/tmp/output.mp3','wb')
+
+    while True:
+        b=fr.read(1024)
+        if b:
+            fw.write(b)
+        else:
+            break
+
+    fr.close()
+    fw.close()
+
+    val=random.randint(1000000,9999999)
+    ros.chdir('/home/jared/to_video')
+    ros.system('./p'+str(avatar)+'.sh '+str(val)+' &')
+
+    while True:
+        print('waiting...')
+        if ros.path.exists('/tmp/results/'+str(val)):
+            break
+        time.sleep(5)
+        print('waiting...')
+
+    fr=conn.builtins.open('/tmp/results/'+str(val)+'.mp4','rb')
+    fw=open(dir_anchor+fileName+".mp4",'wb')
+    while True:
+        b=fr.read(1024)
+        if b:
+            fw.write(b)
+        else:
+            break
+
+    fr.close()
+    fw.close()
+    
+def syllable_count(word):
+    word = word.lower()
+    count = 0
+    vowels = "aeiouy"
+    if word[0] in vowels:
+        count += 1
+    for index in range(1, len(word)):
+        if word[index] in vowels and word[index - 1] not in vowels:
+            count += 1
+
+    if word.endswith("e"):
+        count -= 1
+    if count == 0:
+        count += 1
+    return count
+
+def split_sentence(in_str, maxLen):
+    re.findall(r'[\u4e00-\u9fff]+', in_str)
+
+    zh_idx = []
+    eng_idx= []
+    for i in range(len(in_str)):
+        if in_str[i] > u'\u4e00' and in_str[i] < u'\u9fff':
+            zh_idx.append(i)
+        else:
+            eng_idx.append(i)
+
+    space_index = [m.start() for m in re.finditer(' ', in_str)]
+    for idx in space_index:
+        eng_idx.remove(idx)
+    
+    eng_range_list = []
+    for k, g in groupby(enumerate(eng_idx), lambda ix : ix[0] - ix[1]):
+        eng_range = list(map(itemgetter(1), g))
+        eng_range_list.append(eng_range)
+
+    total_syllable = 0
+    for i in range(len(eng_range_list)):
+        total_syllable += (syllable_count(in_str[eng_range_list[i][0]:eng_range_list[i][-1]+1])+0.5)
+    for i in range(len(zh_idx)):
+        total_syllable+=1
+    
+    #final chchchchchc[en][en][en]
+    #[en] is a vocabulary dict with  occurence of image
+    zh_eng_idx_list = []
+    i = 0
+    while i < len(in_str):
+        if in_str[i]==' ':
+            i+=1
+        if i in zh_idx:
+            zh_eng_idx_list.append(i)
+            i+=1
+        if i in eng_idx:
+            for ls in eng_range_list:
+                if i in ls:
+                    zh_eng_idx_list.append(ls)
+                    i = ls[-1]+1
+                    break
+            
+    zh_eng_dict_list = [{'content':'','time_ratio':0}]
+    idx = 0 
+    current_len = 0
+    sen_idx = 0
+    while idx < len(zh_eng_idx_list):
+        str_from_idx = ''
+        sylla_cnt = 1
+        if type(zh_eng_idx_list[idx])==type([]):
+            str_from_idx = in_str[zh_eng_idx_list[idx][0]:zh_eng_idx_list[idx][-1]+1]+' '
+            sylla_cnt = syllable_count(str_from_idx)
+        else:
+            str_from_idx = in_str[zh_eng_idx_list[idx]]
+    
+      
+        if len(zh_eng_dict_list[sen_idx]['content'])+sylla_cnt>=maxLen:
+            zh_eng_dict_list[sen_idx]['time_ratio'] = current_len/total_syllable
+           
+            zh_eng_dict_list.append({'content':'','time_ratio':0})
+            sen_idx+=1
+            current_len = 0
+        else:
+            current_len += sylla_cnt
+            zh_eng_dict_list[sen_idx]['content'] += str_from_idx
+            idx+=1
+        
+    total_ratio = 0
+    for obj in zh_eng_dict_list:
+        total_ratio+=obj['time_ratio']
+    zh_eng_dict_list[-1]['time_ratio'] = 1-total_ratio
+    return zh_eng_dict_list
+   
+def parse_script(file_path,gt_list):
+    with open(file_path, 'r',encoding="utf-8") as f:
+        raw_lines = [line.strip() for line in f]
+    lines = adjustSub_by_text_similarity(gt_list,raw_lines)
+    text_parser = parser()
+    #make dict
+    dict_list = []
+    for idx in range(len(lines)):
+        script={}
+        rep_ls = text_parser.replace_list(lines[idx])
+        line_content = lines[idx]
+        for reptxt in rep_ls:
+            line_content = line_content.replace(reptxt,'')
+        if len(rep_ls)!=0:
+            script['image_idx'] = int(rep_ls[0].replace('{','').replace('}',''))
+        script['content'] = line_content
+        time_raw = raw_lines[idx * 4 +1 ].split(' --> ')
+        start = time_raw[0].split(':')
+        stop = time_raw[1].split(':')
+        script['start'] = float(start[0])*3600 + float(start[1])*60 + float(start[2].replace(',','.'))
+        script['stop'] = float(stop[0])*3600 + float(stop[1])*60 + float(stop[2].replace(',','.'))
+        dict_list.append(script)
+ 
+    #merge duplicated sentences
+    skip_list = []
+    script_not_dup_list = []
+    for idx in range(len(dict_list)):
+        if idx not in skip_list:
+            dup_list = []
+            found = 0
+            for idx_inner in range(len(dict_list)):
+                if dict_list[idx_inner]['content'] == dict_list[idx]['content'] and idx <= idx_inner:
+                    dup_list.append(idx_inner)
+                    skip_list.append(idx_inner)
+                    found += 1
+                if found != 0 and dict_list[idx_inner]['content']!=dict_list[idx]['content'] and idx <= idx_inner:
+                    found = 0
+                    break
+        
+            for dup_idx in dup_list:
+                if dup_idx == min(dup_list):
+                    dict_list[dup_idx]['type'] = 'lead_sentence'
+                else:
+                    dict_list[dup_idx]['type'] = 'duplicated'
+            dict_list[dup_list[0]]['stop'] = dict_list[dup_list[-1]]['stop']
+          
+            if dict_list[idx]['type'] == 'lead_sentence':
+                script_not_dup_list.append(dict_list[idx])
+            
+    
+    new_idx = 0
+    splitted_dict = []
+    for dic in script_not_dup_list:
+        dic_idx = 0
+        accumulated_duration = 0
+        duration = dic['stop']-dic['start']
+
+        for sub_dic in split_sentence(dic['content'],13):
+            new_dic = {}
+            new_dic['index'] = new_idx
+            if 'image_idx' in dic:
+                new_dic['image_obj'] = {'start':dic['start'],'idx':dic['image_idx']}
+            new_idx+=1
+            ind_duration = duration * sub_dic['time_ratio']
+            new_dic['start'] = dic['start'] + accumulated_duration
+            accumulated_duration += ind_duration
+            new_dic['content'] = sub_dic['content']
+            new_dic['duration'] = ind_duration*0.7
+            splitted_dict.append(new_dic)
+    return splitted_dict
+
+
+
+def adjustSub_by_text_similarity(gts_in,gens_raw):
+    #call by value only
+    gts = gts_in[:]
+    text_parser = parser()
+    for i in range(len(gts)):
+        rep_ls = text_parser.replace_list(gts[i])
+        for reptxt in rep_ls:
+            gts[i] = gts[i].replace(reptxt,'')
+
+    gens = []
+    for idx in range(int((len(gens_raw)+1)/4)):
+        gens.append(gens_raw[idx*4+2])
+
+    combine2 = [''.join([i,j]) for i,j in zip(gts, gts[1:])]
+    combine3 = [''.join([i,j,k]) for i,j,k in zip(gts, gts[1:], gts[2:])]
+    alls = gts #+ combine2 + combine3
+    adjusted = [None]*len(gens)
+    duplicated_list = []
+    for idx in range(len(gens)):
+        match_text = difflib.get_close_matches(gens[idx], alls, cutoff=0.1)
+        if len(match_text) != 0:
+            if match_text[0] not in duplicated_list:
+                adjusted[idx] = match_text[0]
+                duplicated_list.append(match_text[0])
+            else:
+                if match_text[0] == adjusted[idx-1]:
+                    adjusted[idx] = match_text[0]
+                else:
+                    found = 0
+                    for mt in match_text:
+                        if mt not in duplicated_list:
+                            adjusted[idx] = mt
+                            found += 1
+                            break
+                    if found ==0:
+                        adjusted[idx] = ' '
+        else :
+            adjusted[idx] = ' '
+
+    combine2_tag = [''.join([i,j]) for i,j in zip(gts_in, gts_in[1:])]
+    combine3_tag = [''.join([i,j,k]) for i,j,k in zip(gts_in, gts_in[1:], gts_in[2:])]
+    alls_tag = gts_in #+ combine2_tag + combine3_tag
+
+    for idx in range(len(adjusted)):
+        match_text = difflib.get_close_matches(adjusted[idx], alls_tag, cutoff=0.1)
+        adjusted[idx] = match_text[0]
+    return adjusted
+
+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 splitter(s):
+    for sent in re.findall(u'[^!?,。\!\?]+[!? 。\!\?]?', s, flags=re.U):
+        yield sent
+
+def split_by_pun(s):
+    res = list(splitter(s))
+    return res
+
+def generate_subtitle_image_from_dict(name_hash, sub_dict):
+    for script in sub_dict:
+        sv_path = dir_subtitle + name_hash + '/' + str(script['index'])+'.png'
+        sub = script['content']
+        txt2image(sub,sv_path)
+
+def generate_subtitle_image(name_hash,text_content):
+    img_list = [None]*len(text_content)
+    for idx in range(len(text_content)):
+        img_list[idx]=[]
+        senList = split_by_pun(text_content[idx])
+        for inner_idx in range(len(senList)):
+            sv_path = dir_subtitle + name_hash +'/'+str(idx)+ str(inner_idx) +'.png'
+            sub = senList[inner_idx]
+            txt2image(sub,sv_path)
+            img_list[idx]+=[{"count":len(sub),"path":sv_path}]
+    return img_list
+
+def generate_subtitle_image_ENG(name_hash,text_content):
+    img_list = [None]*len(text_content)
+    for idx in range(len(text_content)):
+        sv_path = dir_subtitle + name_hash +'/'+str(idx)+'.png'
+        sub = text_content[idx]
+        txt2image(sub, sv_path,lang='eng')
+        img_list[idx] = sv_path
+    return img_list
+
+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_gen(name_hash,name,text_content, image_urls,multiLang,avatar):
+    file_prepare_long(name, name_hash, text_content,image_urls,multiLang)
+    
+    for fname in range(len(text_content)):
+        call_anchor(name_hash+"/"+str(fname),avatar)
+    print('called............................................')
+    ck=cKey(0,254,0,270)
+    ck_anchor=cKey(0,255,1,320)
+    t = openshot.Timeline(1280, 720, openshot.Fraction(30000, 1000), 44100, 2, openshot.LAYOUT_STEREO)
+    t.Open()
+    main_timer = 0
+    LOGO_OP = openshot.FFmpegReader(dir_video+"LOGO_OP_4.mp4")
+    LOGO_OP.Open()         # Open the reader
+    head_duration = LOGO_OP.info.duration
+    LOGO_OP_clip = video_photo_clip(vid=LOGO_OP,layer=4,position=0,end=head_duration
+                    ,location_y=-0.03,scale_x=0.8,scale_y=0.704)
+    t.AddClip(LOGO_OP_clip)
+    bg_head = openshot.FFmpegReader(dir_video+"complete_head_aispokesgirl.mp4")
+    bg_head.Open()
+    bg_head_clip = video_photo_clip(vid=bg_head,layer=2,position=0,end=LOGO_OP.info.duration,ck=ck)
+    t.AddClip(bg_head_clip)
+    main_timer += head_duration
+    bg_head.Close()
+    LOGO_OP.Close()
+    
+    anchor = openshot.FFmpegReader(dir_anchor+name_hash+"/0.mp4")
+    anchor.Open()
+    #anchor_clip = video_photo_clip(vid=anchor,layer=4,scale_x=0.65,scale_y=0.65,
+    #        location_x=0.35,location_y=0.25,position=main_timer, end=anchor.info.duration,ck=ck_anchor,audio=False)
+    #t.AddClip(anchor_clip)
+
+    speech = openshot.FFmpegReader(dir_sound+name_hash+"/0.mp3")
+    speech.Open()
+    speech_clip = openshot.Clip(speech)
+    speech_clip.Position(main_timer)
+    speech_clip.End(anchor.info.duration)
+    t.AddClip(speech_clip)
+    main_timer += anchor.info.duration
+    anchor.Close()
+    speech.Close()
+    
+    LOGO_ED = openshot.FFmpegReader(dir_video+"LOGO_ED.avi")
+    LOGO_ED.Open()
+    LOGO_ED_clip = video_photo_clip(vid=LOGO_ED,layer=4,position=main_timer,end=LOGO_ED.info.duration
+                    ,location_x=0.005,location_y=-0.031, scale_x=0.8,scale_y=0.6825)
+    t.AddClip(LOGO_ED_clip)
+    main_timer += LOGO_ED.info.duration
+    LOGO_ED.Close()
+    
+    bg = openshot.FFmpegReader(dir_video+"complete_double_aispokesgirl.mp4")
+    bg.Open()
+    bg_times = math.floor(main_timer/bg.info.duration)
+    left_time = (main_timer) % bg.info.duration
+    bg_clip_list = [None] * bg_times
+    bg_list = [None] * bg_times
+    bg.Close()
+    bg_timer = head_duration
+    for idx in range(bg_times):
+        bg_list[idx] = openshot.FFmpegReader(dir_video+"complete_double_aispokesgirl.mp4")
+        bg_list[idx].Open()
+        bg_clip_list[idx] = video_photo_clip(bg_list[idx],layer=2,position=bg_timer,end=bg_list[idx].info.duration,ck=ck)
+        t.AddClip(bg_clip_list[idx])
+        bg_timer += bg_list[idx].info.duration
+        bg_list[idx].Close()
+    bg_left = openshot.FFmpegReader(dir_video+"complete_double_aispokesgirl.mp4")
+    bg_left.Open()
+    bg_left_clip = video_photo_clip(bg_left,layer=2,position=bg_timer,end=left_time,ck=ck)
+    t.AddClip(bg_left_clip)
+    bg_left.Close()
+
+
+    title = openshot.QtImageReader(dir_title+name_hash+".png")
+    title.Open()         # Open the reader
+    title_clip = video_photo_clip(vid=title, layer=4,location_x=-0.047, location_y=0.801,position=0,end=head_duration+main_timer)
+    t.AddClip(title_clip)
+
+    w = video_writer_init(tmp_video_dir+name_hash+"raw.mp4")
+    w.Open()
+    frames = int(t.info.fps)*int(main_timer)
+    for n in range(frames):
+        f=t.GetFrame(n)
+        w.WriteFrame(f)
+    t.Close()
+    w.Close()
+    print(name+"RAW DONE : www.choozmo.com:8168/"+tmp_video_dir+name_hash+"raw.mp4")
+    #start adding sub
+    
+    #add sub
+    Ctr_Autosub.init()
+    Ctr_Autosub.generate_subtitles(tmp_video_dir+name_hash+"raw.mp4",'zh',listener_progress,output=tmp_video_dir+name_hash+"script.txt",concurrency=DEFAULT_CONCURRENCY,subtitle_file_format=DEFAULT_SUBTITLE_FORMAT)
+    
+    sub_dict = parse_script(tmp_video_dir+name_hash+"script.txt",split_by_pun(text_content[0]))
+    for subd in sub_dict:
+        print(subd)
+    
+    generate_subtitle_image_from_dict(name_hash, sub_dict)
+
+    #sv_path = dir_subtitle + name_hash + '/' + str(script['index'])+'.png'
+
+    t = openshot.Timeline(1280, 720, openshot.Fraction(30000, 1000), 44100, 2, openshot.LAYOUT_STEREO)
+    t.Open()
+
+    raw = openshot.FFmpegReader(tmp_video_dir+name_hash+"raw.mp4")
+    raw.Open()
+    raw_clip = video_photo_clip(vid=raw,layer=2,position=0, end=raw.info.duration)
+    t.AddClip(raw_clip)
+    
+
+
+    sub_img_list = [None] * len(sub_dict)
+    sub_clip_list = [None] * len(sub_dict)
+    for sub_obj in sub_dict:
+        idx = int(sub_obj['index'])
+        sub_img_list[idx] = openshot.QtImageReader(dir_subtitle + name_hash + '/' + str(idx)+'.png')
+        sub_img_list[idx].Open()
+        #if sub_obj['duration']>3:
+        #    print('warning')
+        #print('start:',sub_obj['start'],', duration :', sub_obj['duration'],'  content',sub_obj['content'],'idx:',sub_obj['index'])
+        
+        sub_clip_list[idx] = video_photo_clip(vid=sub_img_list[idx], layer=6,location_x=0.069, location_y=0.89,position=sub_obj['start'],end=math.ceil(sub_obj['duration']))
+        t.AddClip(sub_clip_list[idx])
+        sub_img_list[idx].Close()
+
+
+    tp = parser()
+    img_dict_ls = tp.image_clip_info(sub_dict)
+    img_clip_list = [None]*len(listdir(dir_photo+name_hash))
+    img_list = [None]*len(img_clip_list)
+
+    img_file_ls = listdir(dir_photo+name_hash)
+
+    for img_idx in range(len(img_file_ls)):
+        img_list[img_idx] = openshot.FFmpegReader(dir_photo+name_hash+'/'+img_file_ls[img_idx])
+        img_list[img_idx].Open()
+        img_clip_list[img_idx] = video_photo_clip(vid=img_list[img_idx],layer=3
+                ,scale_x=0.81,scale_y=0.68,location_y=-0.03,position=img_dict_ls[img_idx]['start'],end=img_dict_ls[img_idx]['duration'],audio=False)
+        t.AddClip(img_clip_list[img_idx])
+        img_list[img_idx].Close()
+
+    anchor = openshot.FFmpegReader(dir_anchor+name_hash+"/0.mp4")
+    anchor.Open()
+    anchor_clip = video_photo_clip(vid=anchor,layer=4,scale_x=0.65,scale_y=0.65,
+            location_x=0.35,location_y=0.25,position=head_duration, end=anchor.info.duration,ck=ck_anchor,audio=False)
+    t.AddClip(anchor_clip)
+
+
+    w = video_writer_init(tmp_video_dir+name_hash+".mp4")
+    w.Open()
+    frames = int(t.info.fps)*int(main_timer)
+    for n in range(frames):
+        f=t.GetFrame(n)
+        w.WriteFrame(f)
+    t.Close()
+    w.Close()
+
+
+    os.remove(tmp_video_dir+name_hash+"raw.mp4")
+    os.remove(tmp_video_dir+name_hash+"script.txt")
+    print(name+"ALL DONE : www.choozmo.com:8168/"+video_sub_folder+name_hash+"raw.mp4")
+
+
+def anchor_video_v2(name_hash,name,text_content, image_urls,multiLang,avatar):
+
+    file_prepare(name, name_hash, text_content,image_urls,multiLang)
+    sub_list=generate_subtitle_image(name_hash,text_content)
+    
+    for fname in range(len(text_content)):
+        call_anchor(name_hash+"/"+str(fname),avatar)
+        print('step finish')
+    print('called............................................')
+
+    ck=cKey(0,254,0,270)
+    ck_anchor=cKey(0,255,1,320)
+    duration = 0
+    #average layer level is 3
+    t = openshot.Timeline(1280, 720, openshot.Fraction(30000, 1000), 44100, 2, openshot.LAYOUT_STEREO)
+    t.Open()
+
+    main_timer = 0
+    
+    LOGO_OP = openshot.FFmpegReader(dir_video+"LOGO_OP_4.mp4")
+    LOGO_OP.Open()         # Open the reader
+    LOGO_OP_clip = video_photo_clip(vid=LOGO_OP,layer=4,position=0,end=LOGO_OP.info.duration
+                    ,location_y=-0.03,scale_x=0.8,scale_y=0.704)
+    t.AddClip(LOGO_OP_clip)
+    bg_head = openshot.FFmpegReader(dir_video+"complete_head_aispokesgirl.mp4")
+    bg_head.Open()
+    bg_head_clip = video_photo_clip(vid=bg_head,layer=2,position=0,end=LOGO_OP.info.duration,ck=ck)
+    t.AddClip(bg_head_clip)
+    main_timer += LOGO_OP.info.duration
+    head_duration = LOGO_OP.info.duration
+    bg_head.Close()
+    LOGO_OP.Close()
+
+    
+    clip_duration=0
+    photo_clip_list = [None]*len(text_content)
+    img_list = [None]*len(text_content)
+    anchor_clip_list = [None] * len(text_content)
+    anchor_list = [None] * len(text_content)
+    audio_clip_list = [None] * len(text_content)
+    audio_list = [None] * len(text_content)
+    sub_clip_list = [None] * len(text_content)
+    sub_img_list = [None] * len(text_content)
+    
+    idx = 0
+    for p in listdir(dir_photo+name_hash):
+        
+        anchor_list[idx] = openshot.FFmpegReader(dir_anchor+name_hash+"/"+str(idx)+".mp4")
+        clip_duration = anchor_list[idx].info.duration
+        anchor_list[idx].Open()
+        anchor_clip_list[idx] = video_photo_clip(vid=anchor_list[idx],layer=4,scale_x=0.65,scale_y=0.65,
+                location_x=0.35,location_y=0.25,position=main_timer, end=clip_duration,ck=ck_anchor,audio=False)
+        t.AddClip(anchor_clip_list[idx])
+
+        img_list[idx] = openshot.FFmpegReader(dir_photo+name_hash+'/'+p)
+        img_list[idx].Open()
+        photo_clip_list[idx] = video_photo_clip(vid=img_list[idx],layer=3
+                ,scale_x=0.8,scale_y=0.704,location_y=-0.03,position=main_timer,end=clip_duration,audio=False)
+        t.AddClip(photo_clip_list[idx])
+        img_list[idx].Close()
+
+        audio_list[idx] = openshot.FFmpegReader(dir_sound+name_hash+"/"+str(idx)+".mp3")
+        audio_list[idx].Open()
+        audio_clip_list[idx] = openshot.Clip(audio_list[idx])
+        audio_clip_list[idx].Position(main_timer)
+        audio_clip_list[idx].End(clip_duration)
+        t.AddClip(audio_clip_list[idx])
+
+        img_list[idx].Close()
+        anchor_list[idx].Close()
+        audio_list[idx].Close()
+            
+        sub_img_list[idx] = [None] * len(sub_list[idx])
+        sub_clip_list[idx] = [None] * len(sub_list[idx])
+        sub_timer = 0
+        for sub_idx in range(len(sub_list[idx])):
+            sub_img_list[idx][sub_idx] = openshot.QtImageReader(sub_list[idx][sub_idx]['path'])
+            sub_img_list[idx][sub_idx].Open()
+            sub_duration = 0.205*sub_list[idx][sub_idx]['count']
+            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)
+            t.AddClip(sub_clip_list[idx][sub_idx])
+            sub_img_list[idx][sub_idx].Close()
+            sub_timer += sub_duration
+            print(sub_list[idx][sub_idx]['path'])
+        main_timer += clip_duration
+        idx+=1
+    
+    LOGO_ED = openshot.FFmpegReader(dir_video+"LOGO_ED.avi")
+    LOGO_ED.Open()
+    LOGO_ED_clip = video_photo_clip(vid=LOGO_ED,layer=4,position=main_timer,end=LOGO_ED.info.duration+2
+                    ,location_x=0.005,location_y=-0.031
+                    ,scale_x=0.8,scale_y=0.6825)
+    t.AddClip(LOGO_ED_clip)
+    ED_duration = LOGO_ED.info.duration
+    LOGO_ED.Close()
+    
+
+    bg = openshot.FFmpegReader(dir_video+"complete_double_aispokesgirl.mp4")
+    bg.Open()
+    bg_times = math.floor(main_timer+ED_duration/bg.info.duration)
+    left_time = (main_timer+ED_duration) % bg.info.duration
+    bg_clip_list = [None] * bg_times
+    bg_list = [None] * bg_times
+    bg.Close()
+    bg_timer = head_duration
+    for idx in range(bg_times):
+        bg_list[idx] = openshot.FFmpegReader(dir_video+"complete_double_aispokesgirl.mp4")
+        bg_list[idx].Open()
+        bg_clip_list[idx] = video_photo_clip(bg_list[idx],layer=2,position=bg_timer
+                ,end=bg_list[idx].info.duration,ck=ck)
+        t.AddClip(bg_clip_list[idx])
+        bg_timer += bg_list[idx].info.duration
+        bg_list[idx].Close()
+    bg_left = openshot.FFmpegReader(dir_video+"complete_double_aispokesgirl.mp4")
+    bg_left.Open()
+    bg_left_clip = video_photo_clip(bg_left,layer=2,position=bg_timer,end=left_time,ck=ck)
+    t.AddClip(bg_left_clip)
+    bg_left.Close()
+
+    title = openshot.QtImageReader(dir_title+name_hash+".png")
+    title.Open()         # Open the reader
+    title_clip = video_photo_clip(vid=title, layer=4,location_x=-0.047, location_y=0.801,position=0,end=head_duration+main_timer)
+    t.AddClip(title_clip)
+
+    ####start building
+    w = openshot.FFmpegWriter(tmp_video_dir+name_hash+".mp4")
+    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)
+    w.Open()
+    
+    #may change duration into t.info.duration
+    frames = int(t.info.fps)*int(head_duration+main_timer+ED_duration)
+    for n in range(frames):
+        f=t.GetFrame(n)
+        w.WriteFrame(f)
+        
+    #notify_group(name+"的影片已經產生完成囉! www.choozmo.com:8168/"+video_sub_folder+name_hash+".mp4")
+    t.Close()
+    w.Close()
+    print("video at : www.choozmo.com:8168/"+video_sub_folder+name_hash+".mp4")
+
+
+def anchor_video_eng(name_hash,name,text_content, image_urls,sub_titles,avatar):
+    file_prepare(name, name_hash, text_content,image_urls,'eng')
+    sub_list=generate_subtitle_image_ENG(name_hash,sub_titles)
+    
+    for fname in range(len(text_content)):
+        call_anchor(name_hash+"/"+str(fname),avatar)
+        print('step finish')
+    print('called............................................')
+
+    ck=cKey(0,254,0,270)
+    ck_anchor=cKey(0,255,1,320)
+    duration = 0
+    #average layer level is 3
+    t = openshot.Timeline(1280, 720, openshot.Fraction(30000, 1000), 44100, 2, openshot.LAYOUT_STEREO)
+    t.Open()
+
+    main_timer = 0
+    #add logo
+    LOGO_OP = openshot.FFmpegReader(dir_video+"LOGO_OP_4.mp4")
+    LOGO_OP.Open()         # Open the reader
+    LOGO_OP_clip = video_photo_clip(vid=LOGO_OP,layer=4,position=0,end=LOGO_OP.info.duration
+                    ,location_y=-0.03,scale_x=0.8,scale_y=0.704)
+    t.AddClip(LOGO_OP_clip)
+    #add background video  (head is different)
+    bg_head = openshot.FFmpegReader(dir_video+"complete_head_aispokesgirl.mp4")
+    bg_head.Open()
+    bg_head_clip = video_photo_clip(vid=bg_head,layer=2,position=0,end=LOGO_OP.info.duration,ck=ck)
+    t.AddClip(bg_head_clip)
+    
+    main_timer += LOGO_OP.info.duration
+    head_duration = LOGO_OP.info.duration
+    bg_head.Close()
+    LOGO_OP.Close()
+
+    #prepare empty list 
+    clip_duration=0
+    photo_clip_list = [None]*len(text_content)
+    img_list = [None]*len(text_content)
+    anchor_clip_list = [None] * len(text_content)
+    anchor_list = [None] * len(text_content)
+    audio_clip_list = [None] * len(text_content)
+    audio_list = [None] * len(text_content)
+    sub_clip_list = [None] * len(text_content)
+    #openshot image holder
+    sub_img_list = [None] * len(text_content)
+    
+    idx = 0
+    for p in listdir(dir_photo+name_hash):
         
-    for idx in range(len(time_info)-1):
-        time_info[idx]['duration'] = time_info[idx+1]['start']-time_info[idx]['start']
-    time_info[-1]['duration'] = stopPoint
-
-        #index start duration
-    return time_info        
-k=[{'index': 0, 'image_obj': {'start': 4.608, 'idx': 1}, 'start': 4.608, 'content': '露營車可分為拖曳式及自走', 'duration': 2.1504000000000003},
-{'index': 1, 'image_obj': {'start': 4.608, 'idx': 1}, 'start': 7.68, 'content': '式', 'duration': 0.1791999999999999},
-{'index': 2, 'start': 8.192, 'content': '拖曳式即是俗稱「 露營拖', 'duration': 1.5487999999999993},
-{'index': 3, 'start': 10.404571428571428, 'content': '車」 ', 'duration': 0.42239999999999983},
-{'index': 4, 'start': 11.264, 'content': '前方需仰賴母車牽引才能移', 'duration': 2.1504000000000008},
-{'index': 5, 'start': 14.336, 'content': '動', 'duration': 0.17919999999999991},
-{'index': 6, 'image_obj': {'start': 14.848, 'idx': 2}, 'start': 14.848, 'content': '現今市場已有很多小客車都', 'duration': 1.7644307692307686},
-{'index': 7, 'image_obj': {'start': 14.848, 'idx': 2}, 'start': 17.368615384615385, 'content': '附有拖曳功能。 ', 'duration': 1.10276923076923},
-{'index': 8, 'start': 19.456, 'content': '拖車車廂內設備因車主需求', 'duration': 1.9967999999999995},
-{'index': 9, 'start': 22.308571428571426, 'content': '而異', 'duration': 0.33280000000000004},
-{'index': 10, 'image_obj': {'start': 23.04, 'idx': 3}, 'start': 23.04, 'content': '一 般而言大多會有床鋪、 ', 'duration': 1.7740800000000003},
-{'index': 11, 'image_obj': {'start': 23.04, 'idx': 3}, 'start': 25.5744, 'content': '小桌、 冰箱、 迷你廚房', 'duration': 1.6128},
-{'index': 12, 'image_obj': {'start': 23.04, 'idx': 3}, 'start': 27.8784, 'content': '或獨立衛浴等便於露營生活', 'duration': 1.93536},
-{'index': 13, 'image_obj': {'start': 23.04, 'idx': 3}, 'start': 30.6432, 'content': '的各項配備', 'duration': 1.1289600000000004},
-{'index': 14, 'start': 32.512, 'content': '具備動力系統的自走式露營', 'duration': 3.8358486486486485},
-{'index': 15, 'start': 37.99178378378379, 'content': '車價格較高。 ', 'duration': 2.0777513513513512},
-{'index': 16, 'start': 33.28, 'content': '規格有大有小', 'duration': 1.0752000000000008},
-{'index': 17, 'start': 35.072, 'content': '就歐美車款來說', 'duration': 1.254399999999996}]
-ls = image_clip_info(k)
+        anchor_list[idx] = openshot.FFmpegReader(dir_anchor+name_hash+"/"+str(idx)+".mp4")
+        clip_duration = anchor_list[idx].info.duration
+        anchor_list[idx].Open()
+        anchor_clip_list[idx] = video_photo_clip(vid=anchor_list[idx],layer=4,scale_x=0.65,scale_y=0.65,
+                location_x=0.35,location_y=0.25,position=main_timer, end=clip_duration,ck=ck_anchor,audio=False)
+        t.AddClip(anchor_clip_list[idx])
+        #insert image 
+        img_list[idx] = openshot.FFmpegReader(dir_photo+name_hash+'/'+p)
+        img_list[idx].Open()
+        photo_clip_list[idx] = video_photo_clip(vid=img_list[idx],layer=3
+                ,scale_x=0.81,scale_y=0.68,location_y=-0.03,position=main_timer,end=clip_duration,audio=False)
+        t.AddClip(photo_clip_list[idx])
+        img_list[idx].Close()
+        #insert audio (speech)
+        audio_list[idx] = openshot.FFmpegReader(dir_sound+name_hash+"/"+str(idx)+".mp3")
+        audio_list[idx].Open()
+        audio_clip_list[idx] = openshot.Clip(audio_list[idx])
+        audio_clip_list[idx].Position(main_timer)
+        audio_clip_list[idx].End(clip_duration)
+        t.AddClip(audio_clip_list[idx])
+        #insert subtitle
+        sub_img_list[idx] = openshot.QtImageReader(sub_list[idx])
+        sub_img_list[idx].Open()
+        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)
+        t.AddClip(sub_clip_list[idx])
+
+        img_list[idx].Close()
+        anchor_list[idx].Close()
+        audio_list[idx].Close()
+        sub_img_list[idx].Close()
+            
+        main_timer += clip_duration
+        idx+=1
+    
+    LOGO_ED = openshot.FFmpegReader(dir_video+"ED_ENG.mp4")
+    LOGO_ED.Open()
+    LOGO_ED_clip = video_photo_clip(vid=LOGO_ED,layer=4,position=main_timer,end=LOGO_ED.info.duration+2
+                    ,location_x=0.005,location_y=-0.031
+                    ,scale_x=0.8,scale_y=0.6825)
+    t.AddClip(LOGO_ED_clip)
+    ED_duration = LOGO_ED.info.duration
+    LOGO_ED.Close()
+    
+
+    bg = openshot.FFmpegReader(dir_video+"complete_double_aispokesgirl.mp4")
+    bg.Open()
+    bg_times = math.floor(main_timer+ED_duration/bg.info.duration)
+    left_time = (main_timer+ED_duration) % bg.info.duration
+    bg_clip_list = [None] * bg_times
+    bg_list = [None] * bg_times
+    bg.Close()
+    bg_timer = head_duration
+    for idx in range(bg_times):
+        bg_list[idx] = openshot.FFmpegReader(dir_video+"complete_double_aispokesgirl.mp4")
+        bg_list[idx].Open()
+        bg_clip_list[idx] = video_photo_clip(bg_list[idx],layer=2,position=bg_timer
+                ,end=bg_list[idx].info.duration,ck=ck)
+        t.AddClip(bg_clip_list[idx])
+        bg_timer += bg_list[idx].info.duration
+        bg_list[idx].Close()
+    bg_left = openshot.FFmpegReader(dir_video+"complete_double_aispokesgirl.mp4")
+    bg_left.Open()
+    bg_left_clip = video_photo_clip(bg_left,layer=2,position=bg_timer,end=left_time,ck=ck)
+    t.AddClip(bg_left_clip)
+    bg_left.Close()
+
+    title = openshot.QtImageReader(dir_title+name_hash+".png")
+    title.Open()         # Open the reader
+    title_clip = video_photo_clip(vid=title, layer=4,location_x=-0.047, location_y=0.801,position=0,end=head_duration+main_timer)
+    t.AddClip(title_clip)
+
+    ####start building
+    w = openshot.FFmpegWriter(tmp_video_dir+name_hash+".mp4")
+    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)
+    w.Open()
+    
+    #may change duration into t.info.duration
+    frames = int(t.info.fps)*int(head_duration+main_timer+ED_duration)
+    for n in range(frames):
+        f=t.GetFrame(n)
+        w.WriteFrame(f)
+        
+    #notify_group(name+"(ENG)的影片已經產生完成囉! www.choozmo.com:8168/"+video_sub_folder+name_hash+".mp4")
+    t.Close()
+    w.Close()
+    print("video at : www.choozmo.com:8168/"+video_sub_folder+name_hash+".mp4")
+    
+    #line notifs
+
+import pyttsx3
+def make_speech(text):
+    engine = pyttsx3.init()
+    #voices = engine.getProperty('voices')
+    engine.setProperty('voice', 'Mandarin')
+    engine.save_to_file(text, '/app/speech.mp3')
+    engine.runAndWait()
+

+ 1 - 1
etc/Dockerfile

@@ -4,8 +4,8 @@ RUN apt update
 RUN apt install -y software-properties-common
 RUN add-apt-repository -y ppa:openshot.developers/ppa
 RUN apt update
-RUN apt-get update
 RUN DEBIAN_FRONTEND=noninteractive apt-get install keyboard-configuration -y
+RUN apt-get update && apt-get install -y apt-transport-https
 RUN apt-get install python3-pip -y
 RUN apt-get install vim -y
 RUN pip3 install fastapi