Kaynağa Gözat

fix conflict

huaisianhuang 3 yıl önce
ebeveyn
işleme
02a03b5fad

+ 15 - 9
OpenshotService/openshot_video_generator.py

@@ -108,7 +108,7 @@ def make_dir(name_hash):
     except FileExistsError:
         print("~~~~~~Warning~~~~~~~~~Directory " , dir_subtitle+name_hash ,  " already exists")
 
-def file_prepare(name, name_hash,text_content,image_urls,lang='zh'):
+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:
@@ -133,13 +133,17 @@ def file_prepare(name, name_hash,text_content,image_urls,lang='zh'):
         txt_idx+=1
     print("text file made")
     #make mp3
-    language = 'zh-tw'
     txt_idx = 0
     for txt in text_content:
-        if lang!='zh':
-            tts = gTTS(txt)
-            tts.save(dir_sound+name_hash+"/"+str(txt_idx)+".mp3")
+        if lang!='zh' or multiLang==1:
+            if lang!='zh':
+                tts = gTTS(txt)
+                tts.save(dir_sound+name_hash+"/"+str(txt_idx)+".mp3")
+            else:
+                tts = gTTS(txt,lang='zh-tw')
+                tts.save(dir_sound+name_hash+"/"+str(txt_idx)+".mp3")
         else:
+            print('use zhtts')
             tts = zhtts.TTS() 
             tts.text2wav(txt,dir_sound+name_hash+"/"+str(txt_idx)+".mp3")
         txt_idx+=1
@@ -251,10 +255,11 @@ def generate_subtitle_image_ENG(name_hash,text_content):
         img_list[idx] = sv_path
     return img_list
 
-def anchor_video_v2(name_hash,name,text_content, image_urls,avatar):
+def anchor_video_v2(name_hash,name,text_content, image_urls,multiLang,avatar):
     print(os.getcwd())
     print('sub image made')
-    file_prepare(name, name_hash, text_content,image_urls)
+    print(multiLang)
+    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)):
@@ -536,8 +541,9 @@ def anchor_video_eng(name_hash,name,text_content, image_urls,sub_titles,avatar):
 
 
 class video_service(rpyc.Service):
-    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(self,name_hash,name,text_content, image_urls,multiLang,avatar):
+        print('ML:'+str(multiLang))
+        anchor_video_v2(name_hash,name,text_content, image_urls,multiLang,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)
 

+ 79 - 0
api/gSlide.py

@@ -0,0 +1,79 @@
+
+
+from __future__ import print_function
+import os.path
+from googleapiclient.discovery import build
+from google_auth_oauthlib.flow import InstalledAppFlow
+from google.auth.transport.requests import Request
+from google.oauth2.credentials import Credentials
+from google.oauth2 import service_account
+import argparse
+import requests
+import pprint
+import json
+import calendar
+import time
+import os
+import shutil
+from distutils.util import strtobool
+
+SCOPES = ['https://www.googleapis.com/auth/presentations.readonly',
+          'https://www.googleapis.com/auth/drive.metadata.readonly']
+
+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/'
+
+def parse_url(url):
+    #https://docs.google.com/presentation/d/17jJ3OZWh8WorFcolB_LiTa7xQ3R-xrmFlqJ_EyCj06M/edit#slide=id.p
+    return url.split('/')[5]
+
+def parse_slide_url(slide_url,eng):
+    PRESENTATION_ID = parse_url(slide_url)
+    credentials = service_account.Credentials.from_service_account_file('spread2.json')
+    scoped_credentials = credentials.with_scopes(SCOPES)
+    creds = credentials
+
+    notes_list=[]
+    sub_title_list=[]
+    img_list=[]
+
+    service = build('slides', 'v1', credentials=creds)
+        # Call the Slides API
+    presentation = service.presentations().get(
+        presentationId=PRESENTATION_ID).execute()
+    slides = presentation.get('slides')
+        
+    for i, slide in enumerate(slides):
+        # Check if the notes exists
+        if 'text' in slide['slideProperties']['notesPage']['pageElements'][1]['shape'].keys():
+            notes = slide['slideProperties']['notesPage']['pageElements'][1]['shape']['text']['textElements'][1]['textRun']['content']
+            if '[sub_title]' in notes:
+                sub_title = notes.split('[sub_title]')[1].strip()
+                print('Sub_title:',end='')
+                pprint.pprint(sub_title)
+                sub_title_list.append(sub_title)
+                notes = notes.split('[sub_title]')[0].strip()
+            pprint.pprint(notes)
+            notes_list.append(notes)  
+        else:
+            notes_list.append("")
+                
+        # Convert the content of the presentation to png
+        thumbnail = service.presentations().pages().getThumbnail(presentationId=PRESENTATION_ID, pageObjectId=slide['objectId']).execute()
+        pprint.pprint(thumbnail)
+        img_list.append(thumbnail['contentUrl'])
+            
+    # data
+    slide_content = { "name": presentation['title'], "text_content": notes_list, "image_urls": img_list, "avatar": "7", "client_id": calendar.timegm(time.gmtime()) }
+    if eng:
+        slide_content['sub_titles'] = sub_title_list
+    print(slide_content)
+    return slide_content['name'],slide_content['text_content'],slide_content['image_urls']
+        

+ 56 - 9
api/main.py

@@ -40,6 +40,7 @@ import mailer
 from moviepy.editor import VideoFileClip
 import traceback
 import logging
+import gSlide
 pymysql.install_as_MySQLdb()
 
 app = FastAPI()
@@ -75,7 +76,6 @@ video_dest = '/var/www/html/'+video_sub_folder
 avatar_dest = '/var/www/html/'+avatar_sub_folder
 
 
-
 @app.get("/index2")
 async def index2():
     return FileResponse('static/index2.html')
@@ -102,6 +102,15 @@ async def get_home_page(request: Request, response: Response, Authorize: AuthJWT
     current_user = Authorize.get_jwt_subject()
     return templates.TemplateResponse("make_video.html", {"request": request, "response": response})
 
+@app.get("/make_video_slide", response_class=HTMLResponse)
+async def make_video_slide(request: Request, response: Response, Authorize: AuthJWT = Depends()):
+    try:
+        Authorize.jwt_required()
+    except Exception as e:
+        print(e)
+        return '請先登入帳號'
+    current_user = Authorize.get_jwt_subject()
+    return templates.TemplateResponse("make_video_slide.html", {"request": request, "response": response})
 
 @app.get('/user_profile', response_class=HTMLResponse)
 def protected(request: Request, Authorize: AuthJWT = Depends()):
@@ -209,6 +218,41 @@ async def create_upload_file(file: UploadFile = File(...)):
         return {'msg':'檔案無法使用'}
     return {"msg": 'www.choozmo.com:8168/'+tmp_img_sub_folder+img_name+'.jpg'}
 
+@app.post("/make_anchor_video_gSlide")
+async def make_anchor_video_gSlide(req:models.gSlide_req,token: str = Depends(oauth2_scheme)):
+    name, text_content, image_urls = gSlide.parse_slide_url(req.slide_url,eng=False)
+    if len(image_urls) != len(text_content):
+        return {'msg':'副標題數量、圖片(影片)數量以及台詞數量必須一致'}
+    for idx in range(len(image_urls)):
+        if 'http' not in image_urls[idx]:
+            image_urls[idx] = 'http://'+image_urls[idx]
+    if req.multiLang==0:
+        for txt in text_content:
+            if re.search('[a-zA-Z]', txt) !=None:
+                print('語言錯誤')
+                return {'msg':'輸入字串不能包含英文字!'}
+    name_hash = str(time.time()).replace('.','')
+    for imgu in 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}
+    user_id = get_user_id(token)
+    proto_req = models.request_normal()
+    proto_req.text_content = text_content
+    proto_req.name = name
+    proto_req.image_urls = image_urls
+    proto_req.avatar = req.avatar
+    proto_req.multiLang = req.multiLang
+    save_history(proto_req,name_hash,user_id)
+    x = threading.Thread(target=gen_video_queue, args=(name_hash,name, text_content, image_urls,int(req.avatar),req.multiLang,user_id))
+    x.start()
+    return {"msg":"製作影片需要時間,請您耐心等候,成果會傳送至LINE群組中"} 
+
 @app.post("/make_anchor_video")
 async def make_anchor_video(req:models.request,token: str = Depends(oauth2_scheme)):
     if len(req.image_urls) != len(req.text_content):
@@ -216,9 +260,11 @@ async def make_anchor_video(req:models.request,token: str = Depends(oauth2_schem
     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':'輸入字串不能包含英文字!'}
+    if req.multiLang==0:
+        for txt in req.text_content:
+            if re.search('[a-zA-Z]', txt) !=None:
+                print('語言錯誤')
+                return {'msg':'輸入字串不能包含英文字!'}
     name_hash = str(time.time()).replace('.','')
     for imgu in req.image_urls:
         try:
@@ -231,7 +277,7 @@ async def make_anchor_video(req:models.request,token: str = Depends(oauth2_schem
             return {'msg':"無法辨別圖片網址"+imgu}
     user_id = get_user_id(token)
     save_history(req,name_hash,user_id)
-    x = threading.Thread(target=gen_video_queue, args=(name_hash,req.name, req.text_content, req.image_urls,int(req.avatar),user_id))
+    x = threading.Thread(target=gen_video_queue, args=(name_hash,req.name, req.text_content, req.image_urls,int(req.avatar),req.multiLang,user_id))
     x.start()
     return {"msg":"製作影片需要時間,請您耐心等候,成果會傳送至LINE群組中"} 
 
@@ -407,7 +453,7 @@ def gen_video_eng(name_hash,name,text_content, image_urls,sub_titles,avatar):
     shutil.copy(tmp_video_dir+name_hash+'.mp4',video_dest+name_hash+'.mp4')
     os.remove(tmp_video_dir+name_hash+'.mp4')
 
-def gen_video_queue(name_hash,name,text_content, image_urls,avatar,user_id):
+def gen_video_queue(name_hash,name,text_content, image_urls,avatar,multiLang,user_id):
     db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/AI_anchor?charset=utf8mb4')
     time_stamp = datetime.fromtimestamp(time.time()).strftime("%Y-%m-%d %H:%M:%S")
     txt_content_seperate_by_dot = ''
@@ -418,7 +464,8 @@ def gen_video_queue(name_hash,name,text_content, image_urls,avatar,user_id):
     for iurl in image_urls:
         img_urls_seperate_by_dot += iurl+","
     img_urls_seperate_by_dot = img_urls_seperate_by_dot[:-1]
-    db['video_queue'].insert({'name_hash':name_hash,'name':name,'text_content':txt_content_seperate_by_dot,'image_urls':img_urls_seperate_by_dot,'avatar':avatar,'timestamp':time_stamp})
+    
+    db['video_queue'].insert({'name_hash':name_hash,'name':name,'text_content':txt_content_seperate_by_dot,'image_urls':img_urls_seperate_by_dot,'multiLang':multiLang,'avatar':avatar,'timestamp':time_stamp})
     while True:
         
         if first(db.query('SELECT * FROM video_queue_status'))['status'] == 1:#only one row in this table, which is the id 1 one
@@ -434,7 +481,7 @@ def gen_video_queue(name_hash,name,text_content, image_urls,avatar,user_id):
             c = rpyc.connect("localhost", 8858)
             c._config['sync_request_timeout'] = None
             remote_svc = c.root
-            my_answer = remote_svc.call_video(top1['name_hash'],top1['name'],top1['text_content'].split(','), top1['image_urls'].split(','),top1['avatar']) # method call
+            my_answer = remote_svc.call_video(top1['name_hash'],top1['name'],top1['text_content'].split(','), top1['image_urls'].split(','),top1['multiLang'],top1['avatar']) # method call
             shutil.copy(tmp_video_dir+top1['name_hash']+'.mp4',video_dest+top1['name_hash']+'.mp4')
             os.remove(tmp_video_dir+top1['name_hash']+'.mp4')
             vid_duration = VideoFileClip(video_dest+top1['name_hash']+'.mp4').duration
@@ -454,7 +501,7 @@ def gen_video_queue(name_hash,name,text_content, image_urls,avatar,user_id):
             else:
                 left_time = left_time - vid_duration
                 db.query('UPDATE users SET left_time ='+str(left_time)+' WHERE id='+str(user_id)+';')
-                notify_group(name+"的影片已經產生完成囉! www.choozmo.com:8168/"+video_sub_folder+name_hash+".mp4")
+                #notify_group(name+"的影片已經產生完成囉! www.choozmo.com:8168/"+video_sub_folder+name_hash+".mp4")
                 notify_line_user(name+"的影片已經產生完成囉! www.choozmo.com:8168/"+video_sub_folder+name_hash+".mp4", line_token)
         except Exception as e:
             logging.error(traceback.format_exc())

+ 15 - 0
api/models.py

@@ -15,6 +15,21 @@ class request(BaseModel):
     image_urls: List[str]
     avatar: str
     client_id :str
+    multiLang :int
+
+class request_normal():
+    name: str
+    text_content: List[str]
+    image_urls: List[str]
+    avatar: str
+    client_id :str
+    multiLang :int
+
+class gSlide_req(BaseModel):
+    slide_url: str
+    avatar: str
+    client_id :str
+    multiLang :int
 
 class request_eng(BaseModel):
     name: str

+ 12 - 0
api/spread2.json

@@ -0,0 +1,12 @@
+{
+  "type": "service_account",
+  "project_id": "dstest-1-292707",
+  "private_key_id": "41b3cec48b4af2e91b89cf6c1644b2fbdf603a72",
+  "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC47O9j5gNkeWsY\n7XsBwl08z/pLNKrKPq8zYoI2tlnctHAOowjSkwXRAuERUeY+O4cRn8HNRqtnZ5M/\nt3kLEtmQpQClk7A8pv21bo4lT8nILZLWU1ovo3bzFNaBN7I/1PNUzSm8G5w4aUSy\noO2/beQLoz0gs1e1WlLAQVkS4NvUFagP/nY3sAgW/1mZSeNsHk8x8VLc1t6rIlSl\n9wPQ52KBCTUUu6gveQ5nzpOZ3eNxr+ftllF378tt/tzCqFdI0DkYYv7jxSAfXa+y\nmlHoSE8mhd+M4hvrf4E5jjvdfyNmALiBpyEW2YAMe1qK0Ay3aUodZxSIKCU8FYo2\njIVCBxTtAgMBAAECggEAVUdafECx1s9RbrzxaVXHJoiona7rhfnTVMh8URvVo/yH\n4pAXvPf1CjagMRsKKS/NcXixaGdLGxP+KTeEd/FY6KXW/wR1FPtTa6xQm+9IF+rA\nWNs2b1qcO6wj4ZIcPuiG0FgOg4NeDvuopRGmog1cyWsdgOuDqou9NpDMMXMFnS8Y\nivS3P1pKXSbg7XKQ4mCWfQk5Oq8Uf3OrOOQncFvUazWQDnhI7GZ9yLw+pqCeziWv\nUeGql2EKtjEsOj9zaN4AeGoSyZDcgVo3X4XRR+sq/Jqd2D8m0aQmlMEcEIVLPmtH\nkugDJsS+Yuk7YQFX4pMbKOCYlO2XCX1STPAn+ytxFwKBgQDlkMG8dvCAn3qoTjlh\n4qj8AHlcyZghcR9Ru1qnSZbw7wVBiTVD8L6mEiLmEGumkNHnwnE/s0PuU10POXeu\nrD4AYtLnJ5f42ycsbBw+ncc1qCwqBwdx4Vo0QXNBs4S+zmf6M8fuzT5wWnaHP5Tg\nbdKByPlpIoGwy/7XseRi3qkQLwKBgQDOOEK3fZ+PQ7PBCGw8DcfWXL4dsUoa5KuC\ncZbkE+sOyGAGBwN+UvxxLQqkSLDtuki6t9+9DvtkfY5N79JN5fGId+BBg8HdhCI+\nxKElGj52f1lcukhMJ3zhLsbUFWz0UFJDudG+qaUuqHRjVCLRJTzlTrvqjHtYLwyO\nQPTi5QnpowKBgQDlURbWZpGUSrrCCXH0v/BB209gSti2/0Nj552E4lPvVTSQ5Lja\np1AqoI9P9jMy7hNgSbHLCg3fslKRdLyDNfexdwZqdfivVGvrSgtk2UM37EhBq0fa\nkFwFOyQhC2ydFZ50JumfOFMY9KTWMcNL9SiFEPdj+F5I914YpNEZmoaTbQKBgF+8\nXLTEvEONYbD20RCcMS8CRTyRpt6PVFQtmahu2sw1F+cUcHm/2vRLvcoA+SqUNdmB\nLXyerPS9GUhzUsXZP2VkiZbArUrCYgeTz1/jLCZk/r5+uLuqBV6hEas5+yf89gP9\nCzOhnE7p44aNc9B2oiuufqzn5QdOaFzOKSIAxLZTAoGBAIicGUmg/FPXj9TC7/8E\nRX7TBFEmJOt+cQNCQZ2KLJD4Io1v7tISjyv2dkYxQZ2tMRE3uOniphyAJhSypseL\naDGyd4LMEkRp6Tazg71T3nepb10MH4pWsvc0O5bXxxEyLeaF+1gWdN3TMALv3B3H\nqcxxFjhWQ492akdnAKYRUgGf\n-----END PRIVATE KEY-----\n",
+  "client_email": "service@dstest-1-292707.iam.gserviceaccount.com",
+  "client_id": "118117667194503067224",
+  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
+  "token_uri": "https://oauth2.googleapis.com/token",
+  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
+  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/service%40dstest-1-292707.iam.gserviceaccount.com"
+}

+ 36 - 1
api/static/script_util.js

@@ -100,7 +100,9 @@ $(".next").click(function () {
       imgARR.push($(`.imgsrc${i}`).val())
     }
   }
-  dataOBJ = { "name": name_title, "text_content": txtARR, "image_urls": imgARR, "avatar": avatar, "client_id": client_id }
+  multiLang = 0
+  if ($('#multiLang').prop("checked")) {multiLang = 1;}
+  dataOBJ = { "name": name_title, "text_content": txtARR, "image_urls": imgARR, "avatar": avatar,"multiLang":multiLang, "client_id": client_id }
   objstr = JSON.stringify(dataOBJ);
   console.log(dataOBJ)
   jwt_token =  get_jwt_token()
@@ -124,6 +126,39 @@ $(".next").click(function () {
   result = xhr.send(objstr);
 });
 
+const slide_button = document.querySelector('#send_slide');
+$("#send_slide").click(function () {
+  slide_button.setAttribute('disabled', '');
+  setTimeout(function () {
+    slide_button.removeAttribute('disabled')
+  }, 4000);
+  avatar = $('.avatar').val();
+  var step;
+  multiLang = 0
+  if ($('#multiLang').prop("checked")) {multiLang = 1;}
+  dataOBJ = {'slide_url':$('#slide_raw_url').val(),"avatar": avatar,"multiLang":multiLang, "client_id": client_id }
+  objstr = JSON.stringify(dataOBJ);
+  jwt_token =  get_jwt_token()
+  var xhr = new XMLHttpRequest();
+  xhr.open("POST", "/make_anchor_video_gSlide");
+  xhr.setRequestHeader("accept", "application/json");
+  xhr.setRequestHeader("Authorization","Bearer "+jwt_token)
+  xhr.setRequestHeader("Content-Type", "application/json");
+  xhr.onreadystatechange = function () {
+    if (xhr.readyState === 4) {
+      Swal.fire({
+        title: "資料已送出",
+        icon: 'success',
+        text: '資料已傳送,請耐心等候',
+        confirmButtonColor: '#3085d6',
+      });
+    }
+  };
+  var data = renderXHR_data(dataOBJ)
+  console.log(data)
+  result = xhr.send(objstr);
+});
+
 $(".gen_avatar").click(function () {
   dataOBJ = { "imgurl": $('.img_src').val() }
   objstr = JSON.stringify(dataOBJ);

+ 0 - 4
api/static/style.css

@@ -492,10 +492,6 @@ select {
 	border-radius: 5px;
 }
 
-input[type="checkbox"] {
-	display: none;
-}
-
 #checker1, #checker2 {
 	display: inline;
 }

+ 3 - 0
api/templates/index.html

@@ -42,6 +42,9 @@
                     <li class="nav-item">
                         <a class="nav-link active btn-gocreate text-white" aria-current="page" href="/make_video">製作影片</a>
                     </li>
+                    <li class="nav-item">
+                        <a class="nav-link active btn-gocreate text-white" aria-current="page" href="/make_video_slide">SLIDE製作影片</a>
+                    </li>
                 </ul>
                 
                 <ul class="navbar-nav mb-2 mb-lg-0">

+ 27 - 4
api/templates/make_video.html

@@ -127,10 +127,33 @@
                 </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">
+        </fieldset>
+        <fieldset>
+          <h3 class="fs-subtitle">台詞</h3>
+          <label for="myCheck">加入英文:</label> 
+          <input type="checkbox" id="multiLang" >
+          <div class="subtitle-inputs">
+          
+          </div>
+            <span class="add">+</span>
+        </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/>
+          <div class="img-inputs">
+          </div>
+          <span class="addimg">+</span>
+          <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>

+ 149 - 0
api/templates/make_video_slide.html

@@ -0,0 +1,149 @@
+{% extends "index.html" %}
+{% block title %}Login{% endblock %}
+{% block head %}
+{{ super() }}
+{% endblock %}
+{% block content %}
+
+
+
+  <div class="container-fluid">
+    <div id="mySidenav" class="sidenav">
+      <!-- <a href="javascript:void(0)" class="closebtn" onclick="closeNav()">&times;</a> -->
+      <h2 class="go_title" href="/index"><a class="nav-link active" aria-current="page" href="/index">AI Spokes Girl</a></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">SLIDE 連結<img class="ms-1" src="static/img/question.png" alt="" data-bs-toggle="tooltip" data-bs-placement="right" title="將作為影片的內嵌標題"></h3>
+          <input id=slide_raw_url type="text" name='t1' class='title_new' value="" placeholder="標題" /><label for="myCheck">加入英文:</label> 
+          <input type="checkbox" id="multiLang" > <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="7">Peggy</option>
+            <option value="8">Stacy</option>
+            <option value="10">Nina黑</option>
+            <option value="9">Nina灰</option>
+            <option value="11">Summer韓小夏</option>
+            <option value="12">Jocelyn</option>
+            <option value="12">Angela</option>
+          </select>
+          <div class="owl-carousel owl-theme">
+            <div class="card item" 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 item" 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 item" 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 item" 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 class="card item" data-avatar="Summer韓小夏" data-img="summer">
+              <div class="imgfr"><img src="static/img/summer.webp" class="card-img-top" alt="..."></div>
+              <div class="card-body">
+                <h5 class="card-title">Summer韓小夏</h5>
+              </div>
+            </div>
+            <div class="card item" data-avatar="Jocelyn" data-img="Jocelyn">
+              <div class="imgfr"><img src="static/img/Jocelyn.webp" class="card-img-top" alt="..."></div>
+              <div class="card-body">
+                <h5 class="card-title">Jocelyn</h5>
+              </div>
+            </div>
+            <div class="card item" data-avatar="Angela" data-img="Angela">
+              <div class="imgfr"><img src="static/img/Angela.webp" class="card-img-top" alt="..."></div>
+              <div class="card-body">
+                <h5 class="card-title">Angela</h5>
+              </div>
+            </div>
+          </div>
+        </fieldset>
+        <input id="send_slide" type="button" name="next" class="action-button" value="送出" />
+      </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>
+  
+  
+
+  {% endblock %}