瀏覽代碼

eng prototype

ming 3 年之前
父節點
當前提交
78f87d003c
共有 4 個文件被更改,包括 695 次插入37 次删除
  1. 162 32
      OpenshotService/openshot_video_generator.py
  2. 50 5
      api/main.py
  3. 242 0
      api/static/index_eng.html
  4. 241 0
      api/static/script_anchor_eng.js

+ 162 - 32
OpenshotService/openshot_video_generator.py

@@ -18,6 +18,7 @@ import time
 import math
 import dataset
 from datetime import datetime
+from gtts import gTTS
 
 dir_sound = 'mp3_track/'
 dir_photo = 'photo/'
@@ -76,30 +77,6 @@ def myunichchar(unicode_char):
             unicode_char = chr(mb_string[0] << 8 | mb_string[1])
         return unicode_char
 
-
-def file_prepare(name, name_hash,text_content,image_urls):
-    #save image
-    try:
-        os.mkdir(dir_photo+name_hash)
-    except FileExistsError:
-        print("Directory " , dir_photo+name_hash ,  " already exists")
-    img_num = 1
-    for imgu in image_urls:
-        im = Image.open(requests.get(imgu, stream=True).raw)
-        im.save(dir_photo+name_hash+"/"+str(img_num)+".jpg")
-        img_num+=1
-    #save text
-    text_file = open(dir_text+name_hash+".txt", "w")
-    text_file.write(text_content)
-    text_file.close()
-    print("text file made")
-    #make mp3
-    tts = zhtts.TTS() 
-    tts.text2wav(text_content,dir_sound+name_hash+".mp3")
-    print("mp3 file made")
-    #make title as image
-    txt2image(name, dir_title+name_hash+".png")
-
 def get_url_type(url):
     req = urllib.request.Request(url, method='HEAD', headers={'User-Agent': 'Mozilla/5.0'})
     r = urllib.request.urlopen(req)
@@ -131,7 +108,7 @@ def make_dir(name_hash):
     except FileExistsError:
         print("~~~~~~Warning~~~~~~~~~Directory " , dir_subtitle+name_hash ,  " already exists")
 
-def file_prepare_v2(name, name_hash,text_content,image_urls):
+def file_prepare(name, name_hash,text_content,image_urls,lang):
     make_dir(name_hash)
     img_num = 1
     for imgu in image_urls:
@@ -160,8 +137,12 @@ def file_prepare_v2(name, name_hash,text_content,image_urls):
     language = 'zh-tw'
     txt_idx = 0
     for txt in text_content:
-        tts = zhtts.TTS() 
-        tts.text2wav(txt,dir_sound+name_hash+"/"+str(txt_idx)+".mp3")
+        if lang==1:
+            tts = gTTS(txt)
+            tts.save(dir_sound+name_hash+"/"+str(txt_idx)+".mp3")
+        else:
+            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
@@ -176,6 +157,7 @@ def txt2image(content, save_target):
     text= unicode_text
     draw.text((5,5), text, (255, 255, 0), font)
     canvas.save(save_target, "PNG")
+
 def txt2image_title(content, save_target):
     unicode_text = trim_punctuation(content)
     font = ImageFont.truetype(font="font/DFT_B7.ttc", size=28)
@@ -253,12 +235,20 @@ def generate_subtitle_image(name_hash,text_content):
             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)):
+        img_list[idx]=[]
+        sv_path = dir_subtitle + name_hash +'/'+str(idx)+ str(inner_idx) +'.png'
+        sub = text_content[idx]
+        txt2image(sub, sv_path)
+        img_list[idx] = sub
+    return img_list
 
-
-def anchor_video_v2(name_hash,name,text_content, image_urls,avatar,client_id):
+def anchor_video_v2(name_hash,name,text_content, image_urls,avatar):
     print(os.getcwd())
     print('sub image made')
-    file_prepare_v2(name, name_hash, text_content,image_urls)
+    file_prepare(name, name_hash, text_content,image_urls,0)
     sub_list=generate_subtitle_image(name_hash,text_content)
     
     for fname in range(len(text_content)):
@@ -398,12 +388,152 @@ def anchor_video_v2(name_hash,name,text_content, image_urls,avatar,client_id):
     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,1)
+    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.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+"bg_head.avi")
+    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):
+        
+        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+"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+"bg.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+"bg.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+"bg.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
 
 
 class video_service(rpyc.Service):
-    def exposed_call_video(self,name_hash,name,text_content, image_urls,avatar,client_id):
-        anchor_video_v2(name_hash,name,text_content, image_urls,avatar,client_id)
+    def exposed_call_video(self,name_hash,name,text_content, image_urls,avatar):
+        anchor_video_v2(name_hash,name,text_content, image_urls,avatar)
+    def exposed_call_video_eng(self,name_hash,name,text_content, image_urls,sub_titles,avatar):
+        anchor_video_eng(name_hash,name,text_content, image_urls,sub_titles,avatar)
 
 
 

+ 50 - 5
api/main.py

@@ -70,11 +70,23 @@ class request(BaseModel):
     avatar: str
     client_id :str
 
+class request_eng(BaseModel):
+    name: str
+    text_content: List[str]
+    image_urls: List[str]
+    sub_titles: List[str]
+    avatar: str
+    client_id :str
+
 
 @app.get("/index2")
 async def index2():
     return FileResponse('static/index2.html')
 
+@app.get("/index_eng")
+async def index2():
+    return FileResponse('static/index_eng.html')
+
 @app.get("/gen_avatar")
 async def avatar():
     return FileResponse('static/gen_avatar.html')
@@ -129,10 +141,33 @@ async def make_anchor_video_v2(req:request):
                 im= im.convert("RGB")
         except:
             return {'msg':"無法辨別圖片網址"+imgu}
-    
-    
+
+    save_history(req,name_hash)
+    x = threading.Thread(target=gen_video, args=(name_hash,req.name, req.text_content, req.image_urls,int(req.avatar)))
+    x.start()
+    return {"msg":"製作影片需要時間,請您耐心等候,成果會傳送至LINE群組中"} 
+
+@app.post("/make_anchor_video_eng")
+async def make_anchor_video_eng(req:request_eng):
+    for idx in range(len(req.image_urls)):
+        if 'http' not in req.image_urls[idx]:
+            req.image_urls[idx] = 'http://'+req.image_urls[idx]
+    for txt in req.text_content:
+        if re.search('[a-zA-Z]', txt) !=None:
+            return {'msg':'輸入字串不能包含英文字!'}
+    name_hash = str(time.time()).replace('.','')
+    for imgu in req.image_urls:
+        try:
+            if get_url_type(imgu) =='video/mp4':
+                r=requests.get(imgu)
+            else:
+                im = Image.open(requests.get(imgu, stream=True).raw)
+                im= im.convert("RGB")
+        except:
+            return {'msg':"無法辨別圖片網址"+imgu}
+
     save_history(req,name_hash)
-    x = threading.Thread(target=gen_video, args=(name_hash,req.name, req.text_content, req.image_urls,int(req.avatar),req.client_id))
+    x = threading.Thread(target=gen_video_eng, args=(name_hash,req.name, req.text_content, req.image_urls,req.sub_titles,int(req.avatar)))
     x.start()
     return {"msg":"製作影片需要時間,請您耐心等候,成果會傳送至LINE群組中"} 
 
@@ -174,12 +209,22 @@ def notify_group(msg):
         }
         params = {"message": msg}   
         r = requests.post("https://notify-api.line.me/api/notify",headers=headers, params=params)
-def gen_video(name_hash,name,text_content, image_urls,avatar,client_id):
+def gen_video(name_hash,name,text_content, image_urls,avatar):
+    c = rpyc.connect("localhost", 8878)
+    c._config['sync_request_timeout'] = None
+    remote_svc = c.root
+    
+    my_answer = remote_svc.call_video(name_hash,name,text_content, image_urls,avatar) # method call
+   
+    shutil.copy(tmp_video_dir+name_hash+'.mp4',video_dest+name_hash+'.mp4')
+    os.remove(tmp_video_dir+name_hash+'.mp4')
+
+def gen_video_eng(name_hash,name,text_content, image_urls,sub_titles,avatar):
     c = rpyc.connect("localhost", 8878)
     c._config['sync_request_timeout'] = None
     remote_svc = c.root
     
-    my_answer = remote_svc.call_video(name_hash,name,text_content, image_urls,avatar,client_id) # method call
+    my_answer = remote_svc.call_video_eng(name_hash,name,text_content, image_urls,sub_titles,avatar) # method call
    
     shutil.copy(tmp_video_dir+name_hash+'.mp4',video_dest+name_hash+'.mp4')
     os.remove(tmp_video_dir+name_hash+'.mp4')

+ 242 - 0
api/static/index_eng.html

@@ -0,0 +1,242 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>AI ANCHOR GO</title>
+  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css">
+  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" integrity="sha384-DNOHZ68U8hZfKXOrtjWvjxusGo9WQnrNx2sqG0tfsghAvtVlRW3tvkXWZh58N9jp"
+    crossorigin="anonymous">
+  <link rel="stylesheet"
+    href="https://use.fontawesome.com/releases/v5.1.0/css/all.css" integrity="sha384-lKuwvrZot6UHsBSfcMvOkWwlCMgc0TaWr+30HWe3a4ltaBwTZhyTEggF5tJv8tbt"
+    crossorigin="anonymous">
+    <link rel="preconnect" href="https://fonts.googleapis.com">
+    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+    <link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap" rel="stylesheet"> 
+  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous">
+  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11.0.18/dist/sweetalert2.min.css">
+  <link rel="stylesheet" href="static/style.css">
+  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
+  <style>
+    body {
+      font-family: "Lato", sans-serif;
+    }
+    .sidenav {
+      height: 100%;
+      width: 250px;
+      position: fixed;
+      z-index: 1;
+      top: 0;
+      left: 0;
+      background: linear-gradient(to bottom, #1C7CE0, #150051);
+      overflow-x: hidden;
+      transition: 0.5s;
+      padding-top: 20px;
+    }
+    
+    .sidenav a {
+      padding: 8px 8px 8px 32px;
+      text-decoration: none;
+      font-size: 25px;
+      color: #818181;
+      display: block;
+      transition: 0.3s;
+    }
+    
+    .sidenav a:hover {
+      color: #f1f1f1;
+    }
+    
+    .sidenav .closebtn {
+      position: absolute;
+      top: 0;
+      right: 25px;
+      font-size: 36px;
+      margin-left: 50px;
+    }
+    
+    @media screen and (max-height: 450px) {
+      .sidenav {padding-top: 15px;}
+      .sidenav a {font-size: 18px;}
+    }
+    </style>
+</head>
+<body>
+  <div class="container-fluid">
+    <div id="mySidenav" class="sidenav">
+      <!-- <a href="javascript:void(0)" class="closebtn" onclick="closeNav()">&times;</a> -->
+      <h2 class="go_title">AI ANCHOR GO</h2>
+      <ul class="nav-list">
+        <li class="nav-list-item pb-1 mb-3" data-bs-toggle="modal" data-bs-target="#howto"><i class="fas fa-book-open me-2"></i>使用說明</li>
+        <li class="nav-list-item pb-1" data-bs-toggle="modal" data-bs-target="#history" onclick="openNav()"><i class="fas fa-history me-2"></i>歷史紀錄</li>
+      </ul>
+      <p class="right-text text-white d-inline-block">Choozmo All Rights Reserved</p>
+    </div>
+    <!-- <span style="font-size:30px;cursor:pointer" onclick="openNav()">&#9776; 過去紀錄</span> -->
+    <div class="content ms-auto">
+      <form id="msform">
+        <div class="linker__box">
+          <p>預覽影片</p>
+          <i class="fas fa-link"></i>
+          <a id='linker' style="display: none;" class="ms-2">影片連結</a>
+        </div>
+        <!-- fieldsets -->
+        <fieldset>
+          <h3 class="fs-subtitle">標題<img class="ms-1" src="static/img/question.png" alt="" data-bs-toggle="tooltip" data-bs-placement="right" title="將作為影片的內嵌標題"></h3>
+          <input id=title type="text" name='t1' class='title_new' value="" placeholder="標題" /> <br/>
+        </fieldset>
+        <fieldset>
+          <h3  class="fs-subtitle">選擇人物<img class="ms-1" src="static/img/question.png" alt="" data-bs-toggle="tooltip" data-bs-placement="right" title="將作為影片的講者"></h3>
+          <select id="avatar" class='avatar'>
+            <option value="6">Angus</option>
+            <option value="7">Peggy</option>
+            <option value="8">Stacy</option>
+            <option value="10">Nina黑</option>
+            <option value="9">Nina灰</option>
+          </select>
+          <div class="d-flex">
+            <div class="card" style="width:25%;" data-avatar="Angus" data-img="angus">
+              <div class="imgfr"><img src="static/img/angus.webp" class="card-img-top" alt="..."></div>
+              <div class="card-body">
+                <h5 class="card-title">Angus</h5>
+              </div>
+            </div>
+            <div class="card" style="width:25%;" data-avatar="Peggy" data-img="peggy">
+              <div class="imgfr"><img src="static/img/peggy.webp" class="card-img-top" alt="..."></div>
+              <div class="card-body">
+                <h5 class="card-title">Peggy</h5>
+              </div>
+            </div>
+            <div class="card" style="width:25%;" data-avatar="Stacy" data-img="stacy">
+              <div class="imgfr"><img src="static/img/stacy.webp" class="card-img-top" alt="..."></div>
+              <div class="card-body">
+                <h5 class="card-title">Stacy</h5>
+              </div>
+            </div>
+            <div class="card" style="width:25%;" data-avatar="Nina黑" data-img="ninablack">
+              <div class="imgfr"><img src="static/img/ninablack.webp" class="card-img-top" alt="..."></div>
+              <div class="card-body">
+                <h5 class="card-title">Nina黑</h5>
+              </div>
+            </div>
+            <div class="card" style="width:25%;" data-avatar="Nina灰" data-img="ninawhite">
+              <div class="imgfr"><img src="static/img/ninawhite.webp" class="card-img-top" alt="..."></div>
+              <div class="card-body">
+                <h5 class="card-title">Nina灰</h5>
+              </div>
+            </div>
+          </div>
+        </fieldset>
+        <fieldset>
+          <h3 class="fs-subtitle">台詞</h3>
+          <input type="text" name='t1' class='txtsrc txtsrc1' value="" placeholder="1"/> <br/>
+          <input type="text" name='t2' class='txtsrc txtsrc2' value="" placeholder="2" /><br/>
+          <input type="text" name='t3' class='txtsrc txtsrc3' value="" placeholder="3" /><br/>
+          <input type="text" name='t4' class='txtsrc txtsrc4' value="" placeholder="4" /><br/>
+          <input type="text" name='t5' class='txtsrc txtsrc5' value="" placeholder="5" /><br/>
+          <input type="text" name='t6' class='txtsrc txtsrc6' value="" placeholder="6" /><br/>
+          <input type="text" name='t7' class='txtsrc txtsrc7' value="" placeholder="7" /><br/>
+          <input type="text" name='t8' class='txtsrc txtsrc8' value="" placeholder="8" /><br/>
+          <input type="text" name='t9' class='txtsrc txtsrc9' value="" placeholder="9" /><br/>
+          <input type="text" name='t10' class='txtsrc txtsrc10' value="" placeholder="10" /><br/>
+        </fieldset>
+        <fieldset>
+          <h3 class="fs-subtitle">副標題</h3>
+          <input type="text" class='sub_text'  placeholder="1"/> <br/>
+          <input type="text" class='sub_text'  placeholder="2" /><br/>
+          <input type="text" class='sub_text'  placeholder="3" /><br/>
+          <input type="text" class='sub_text'  placeholder="4" /><br/>
+          <input type="text" class='sub_text'  placeholder="5" /><br/>
+          <input type="text" class='sub_text'  placeholder="6" /><br/>
+          <input type="text" class='sub_text'  placeholder="7" /><br/>
+          <input type="text" class='sub_text'  placeholder="8" /><br/>
+          <input type="text" class='sub_text'  placeholder="9" /><br/>
+          <input type="text" class='sub_text'  placeholder="10" /><br/>
+        </fieldset>
+        <fieldset id='imgSrc'>
+          <h3 class="fs-subtitle" style="display: inline-block;">影像連結<img class="ms-1" src="static/img/question.png" alt="" data-bs-toggle="tooltip" data-bs-placement="right" title="僅接受png, jpg, mp4格式"></h3><br/>
+          <input type="text" name='m1' class='imgsrc imgsrc1' value="" placeholder="1" /><input id="img1" type="file" class="img_uploader img_up"><label for="img1" class="upload-btn">上傳檔案</label><br/>
+          <input type="text" name='m2' class='imgsrc imgsrc2' value="" placeholder="2" /><input id="img2" type="file" class="img_uploader img_up"><label for="img2" class="upload-btn">上傳檔案</label><br/>
+          <input type="text" name='m3' class='imgsrc imgsrc3' value="" placeholder="3" /><input id="img3" type="file" class="img_uploader img_up"><label for="img3" class="upload-btn">上傳檔案</label><br/>
+          <input type="text" name='m4' class='imgsrc imgsrc4' value="" placeholder="4" /><input id="img4" type="file" class="img_uploader img_up"><label for="img4" class="upload-btn">上傳檔案</label><br/>
+          <input type="text" name='m5' class='imgsrc imgsrc5' value="" placeholder="5" /><input id="img5" type="file" class="img_uploader img_up"><label for="img5" class="upload-btn">上傳檔案</label><br/>
+          <input type="text" name='m6' class='imgsrc imgsrc6' value="" placeholder="6" /><input id="img6" type="file" class="img_uploader img_up"><label for="img6" class="upload-btn">上傳檔案</label><br/>
+          <input type="text" name='m7' class='imgsrc imgsrc7' value="" placeholder="7" /><input id="img7" type="file" class="img_uploader img_up"><label for="img7" class="upload-btn">上傳檔案</label><br/>
+          <input type="text" name='m8' class='imgsrc imgsrc8' value="" placeholder="8" /><input id="img8" type="file" class="img_uploader img_up"><label for="img8" class="upload-btn">上傳檔案</label><br/>
+          <input type="text" name='m9' class='imgsrc imgsrc9' value="" placeholder="9" /><input id="img9" type="file" class="img_uploader img_up"><label for="img9" class="upload-btn">上傳檔案</label><br/>
+          <input type="text" name='m10' class='imgsrc imgsrc10' value="" placeholder="10" /><input id="img10" type="file" class="img_uploader img_up"><label for="img10" class="upload-btn">上傳檔案</label><br/>
+          <input id="checker" type="button" name="next" class="next action-button" value="送出" />
+          <h3 style="display: none;" class="fs-subtitle">處理進度</h3>
+          <div style="display: none;" id="myProgress">
+            <div style="display: none;" id="myBar">0%</div>
+          </div>
+        </fieldset>
+      </form>
+      <!-- <div style="width: 80%;margin: 0 auto;"><iframe src="http://www.choozmo.com:8168/ai_anchor_video/16250306886652043.mp4" frameborder="0" style="width: 100%;height: 400px;"></iframe></div> -->
+    </div>
+    
+    <div class="modal fade" tabindex="-1" id="howto" aria-labelledby="howto" aria-hidden="true">
+      <div class="modal-dialog">
+        <div class="modal-content">
+          <div class="modal-header">
+            <h5 class="modal-title" id="staticBackdropLabel">使用說明</h5>
+            <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+          </div>
+          <div class="modal-body">
+              <div class="modal-terms">
+                  <ol class="ps-0">
+                      <li>1. 一句台詞請對應提供一個影像連結做為搭配</li>
+                      <li>2. 影像連結檔案格式支援:<stong class="strong">.png, jpg, .mp4</stong></li>
+                      <li>3. 點選“送出”之後需等待一段影片製作的時間,請您耐心等候,待製作完畢可於通知網址查看</li>
+                  </ol>
+              </div>
+          </div>
+        </div>
+      </div>
+  </div> 
+  <div class="modal" tabindex="-1" id="history" aria-labelledby="history" aria-hidden="true">
+    <div class="modal-dialog modal-dialog-scrollable">
+      <div class="modal-content">
+        <div class="modal-header">
+          <h5 class="modal-title" id="staticBackdropLabel">歷史紀錄</h5>
+          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+        </div>
+        <div class="modal-body">
+            <div class="modal-terms">
+              <div class="loader"><img src="static/img/bx_loader.gif" alt=""></div>
+              <ol class="ps-0 historyList">
+              </ol>
+            </div>
+        </div>
+      </div>
+    </div>
+  </div>
+  <div class="modal" tabindex="-1"  id="avatarmega" aria-labelledby="history" aria-hidden="true">
+    <div class="modal-dialog modal-dialog-centered">
+      <div class="modal-content text-center">
+        <div class="modal-header">
+          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"><img src="static/img/close.svg" alt=""></button>
+        </div>
+        <div class="modal-body">
+          <img class="modal-img" src="" alt="">
+          <h5 class="modal-title mt-2"></h5>
+        </div>
+      </div>
+    </div>
+  </div>
+  </div>
+  
+  <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>
+  <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js'></script>
+  <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js" integrity="sha384-IQsoLXl5PILFhosVNubq5LC7Qb9DXgDA9i+tQ8Zj3iwWAwPtgFTxbJ8NT4GN1R8p" crossorigin="anonymous"></script>
+  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.min.js" integrity="sha384-Atwg2Pkwv9vp0ygtn1JAojH0nYbwNJLPhwyoVbhoPwBhjQPR5VtM2+xf0Uwh9KtT" crossorigin="anonymous"></script> 
+  <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.0.18/dist/sweetalert2.all.min.js"></script>
+  <script src="//cdnjs.cloudflare.com/ajax/libs/validate.js/0.13.1/validate.min.js"></script>
+  <script src="static/script_anchor_eng.js"></script>
+
+  <body>
+
+</div>
+
+</body>
+</html>

+ 241 - 0
api/static/script_anchor_eng.js

@@ -0,0 +1,241 @@
+var client_id = Date.now()
+var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
+var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
+  return new bootstrap.Tooltip(tooltipTriggerEl)
+});
+var myModal = new bootstrap.Modal(document.getElementById('history'), {
+  keyboard: false
+})
+var avatarModal = new bootstrap.Modal(document.getElementById('avatarmega'), {
+  keyboard: false
+})
+var modalImg = document.querySelector("#avatarmega .modal-img");
+var modalTitle = document.querySelector("#avatarmega .modal-title");
+var avatarSelector = document.getElementById("avatar");
+var card = document.getElementsByClassName('card');
+card = [... card];
+avatarSelector.addEventListener('change', avatarChange);
+avatarChange();
+
+function addCardListener() {
+  for(let i = 0;i < card.length; i++){
+    card[i].addEventListener('click', openavatarModel);
+  }
+}
+
+addCardListener();
+
+function avatarChange() {
+  var value = avatarSelector.options[avatarSelector.selectedIndex].text;
+  console.log(value);
+  for(let i = 0;i < card.length; i++) {
+    if(card[i].dataset.avatar == value) {
+      card[i].classList.add('active');
+    } else {
+      card[i].classList.remove('active');
+    }
+  }
+}
+
+function openavatarModel() {
+  console.log(this.dataset.img);
+  modalImg.setAttribute("src", `static/img/${this.dataset.img}.webp`);
+  modalTitle.textContent = `${this.dataset.avatar}`;
+  avatarModal.show();
+}
+
+$('input[type=file]').on('change', prepareUpload);
+
+// Grab the files and set them to our variable
+function prepareUpload(event) {
+  files = event.target.files;
+  var data = new FormData();
+  //data.append('file', $('.img_up1').prop('files')[0]);
+  data.append('file', files[0]);
+  // append other variables to data if you want: data.append('field_name_x', field_value_x);
+  $(this).next().text('');
+  $(this).next().html('<img src="static/img/Spinner-1s-181px.gif">');
+  $.ajax({
+    type: 'POST',
+    processData: false, // important
+    contentType: false, // important
+    data: data,
+    url: 'http://www.choozmo.com:8888/uploadfile',
+    dataType: 'json',
+    success: function (jsonData) {
+      event.target.previousSibling.value =jsonData.msg;
+      $(this).prev().val(jsonData.msg);
+      event.target.nextSibling.innerHTML = '';
+      event.target.nextSibling.textContent = '上傳檔案';
+      //console.log($(this).next());
+      //$(this).next().html('上傳檔案');
+      //$(this).next().text('上傳檔案');
+    },
+    error: function (error) {
+      event.target.nextSibling.innerHTML = '';
+      event.target.nextSibling.textContent = '上傳檔案';
+      alert('圖片錯誤');
+    }
+  });
+}
+const button = document.querySelector('.next');
+
+$(".next").click(function () {
+  button.setAttribute('disabled', '');
+  setTimeout(function () {
+    button.removeAttribute('disabled')
+  }, 4000);
+  avatar = $('.avatar').val();
+  name_title = $('.title_new').val();
+  txtARR = [];
+  imgARR = [];
+  subtitleARR = [];
+  var step;
+  for (step = 1; step <= 10; step++) {
+    if ($(".txtsrc" + step).val() != "") {
+      txtARR.push($(".txtsrc" + step).val())
+    }
+  }
+  var step2;
+  for (step2 = 1; step2 <= 10; step2++) {
+    if ($(".imgsrc" + step2).val() != "") {
+      imgARR.push($(".imgsrc" + step2).val())
+    }
+  }
+  for (let i = 0; i < 10; i++) {
+    var stitles = document.getElementsByClassName('sub_text')[i].value
+    if (stitles != "") {
+      subtitleARR.push(stitles)
+    }
+  }
+  dataOBJ = { "name": name_title, "text_content": txtARR, "image_urls": imgARR, "subTitle":subtitleARR, "avatar": avatar, "client_id": client_id }
+  objstr = JSON.stringify(dataOBJ);
+  console.log(dataOBJ)
+  //alert('資料已送出! 請耐心等候')
+  $.ajax({
+    url: 'http://www.choozmo.com:8888/make_anchor_video_v2',
+    //url: 'http://www.choozmo.com:8888/qqreq',
+    dataType : 'json', // 預期從server接收的資料型態
+    contentType : 'application/json; charset=utf-8', // 要送到server的資料型態
+    type: 'post',
+    data: objstr,
+    success: function(suc_data) {
+      Swal.fire({
+        title: "資料已送出",
+        icon: 'success',
+        text: `${suc_data.msg}`,
+        confirmButtonColor: '#3085d6',
+      });  
+      },
+    //data:JSON.stringify({n1:"12",n2:"22"}),
+    error: function (error) {
+      console.error(error)
+    }
+  });
+  
+  });
+
+$(".gen_avatar").click(function () {
+
+  dataOBJ = { "imgurl": $('.img_src').val() }
+  objstr = JSON.stringify(dataOBJ);
+  console.log(dataOBJ)
+  //alert('資料已送出! 請耐心等候')
+  $.ajax({
+    url: 'http://www.choozmo.com:8888/swapFace',
+    dataType: 'json', // 預期從server接收的資料型態
+    contentType: 'application/json; charset=utf-8', // 要送到server的資料型態
+    type: 'post',
+    data: objstr,
+    success: function (suc_data) {
+      alert(suc_data.msg)
+    },
+    //data:JSON.stringify({n1:"12",n2:"22"}),
+    error: function (error) {
+      console.error(error)
+    }
+  });
+
+});
+
+var loaded_data = ''
+function openNav() {
+  document.getElementById("mySidenav").style.width = "250px";
+  document.querySelector('.loader').style.display = "block";
+  $.get("http://www.choozmo.com:8888/history_input", function (data, status) {
+    console.log(data)
+    loaded_data = data
+    for (var obj of data) {
+      var historyList = document.querySelector('.historyList');
+      var list = document.createElement('li');
+      list.id = obj.id;
+      // div-imgfr
+      var divImgfr = document.createElement('div');
+      divImgfr.classList.add('item_imgfr');
+      var img = document.createElement('img');
+      img.setAttribute('src', obj['image_urls'][0]);
+      divImgfr.appendChild(img);
+      // div-content
+      var contentBox = document.createElement('div');
+      contentBox.classList.add('content-box');
+      var boxTitle = document.createElement('p');
+      boxTitle.classList.add('box-title');
+      boxTitle.textContent = obj.name;
+      boxTitle.id = obj.id;
+      boxTitle.setAttribute('onclick', 'load_data()');
+
+      var boxLink = document.createElement('span');
+      boxLink.classList.add('box-link');
+      boxLink.setAttribute("data-url", obj.link);
+      boxLink.setAttribute('onclick', 'view()');
+      boxLink.innerHTML = '<i class="fas fa-play-circle me-1"></i>觀看影片';
+      contentBox.appendChild(boxTitle);
+      contentBox.appendChild(boxLink);
+      list.classList.add("historyList-item");
+      list.setAttribute('onclick', 'load_data()');
+      list.appendChild(divImgfr);
+      list.appendChild(contentBox);
+      historyList.appendChild(list);
+    }
+    document.querySelector('.loader').style.display = "none";
+  });
+}
+function closeNav() {
+  document.getElementById("mySidenav").style.width = "250px";
+}
+
+function view() {
+  event.stopPropagation();
+  console.log(event.target);
+  if(event.target.nodeName === 'I') {
+    return;
+  } else {
+    window.open(`http://${event.target.dataset.url}`, '_blank');
+  }
+}
+
+
+function load_data() {
+  var title = document.getElementById("title");
+  var linker = document.getElementById("linker");
+
+  myModal.hide()
+  tid = event.srcElement.id
+  console.log(tid);
+  linker.setAttribute('href', `http://${loaded_data.find(item => item.id == tid).link}`)
+  linker.setAttribute('target', '_blank')
+  $("#linker").html(`http://${loaded_data.find(item => item.id == tid).link}`)
+  $("#linker").show();
+  $(".linker__box").show();
+
+  $(".title_new").val(loaded_data.find(item => item.id == tid).name)
+  var step;
+  for (step = 1; step <= 10; step++) {
+    $(".txtsrc" + step).val(loaded_data.find(item => item.id == tid).text_content[step - 1])
+  }
+  var step2;
+  for (step2 = 1; step2 <= 10; step2++) {
+    $(".imgsrc" + step2).val(loaded_data.find(item => item.id == tid).image_urls[step2 - 1])
+  }
+
+}