浏览代码

merge from origin AI_Anchor

ming 3 年之前
父节点
当前提交
d32d6f8a77

+ 33 - 17
OpenshotService/openshot_video_generator.py

@@ -434,7 +434,12 @@ def parse_script(file_path,gt_list):
             new_dic['start'] = dic['start'] + accumulated_duration
             accumulated_duration += ind_duration
             new_dic['content'] = sub_dic['content']
-            new_dic['duration'] = ind_duration*0.7
+            new_dic['duration'] = ind_duration*0.9
+            if new_dic['duration'] > 3:
+                print('-----------------------------')
+                print('origin duration : ', duration)
+                print(dic)
+                print('-----------------------------')
             splitted_dict.append(new_dic)
     
     return splitted_dict
@@ -449,11 +454,11 @@ def adjustSub_by_text_similarity(gts_in,gens_raw):
         rep_ls = text_parser.replace_list(gts[i])
         for reptxt in rep_ls:
             gts[i] = gts[i].replace(reptxt,'')
-    print(gts)
+
     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
@@ -470,12 +475,17 @@ def adjustSub_by_text_similarity(gts_in,gens_raw):
         else:
             adjusted[idx] = match_text[0]
             duplicated_list.append(match_text[0])
+        if None == adjusted[idx]:
+            adjusted[idx] = gens[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):
@@ -526,8 +536,6 @@ def video_writer_init(path):
         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)
     
@@ -643,6 +651,10 @@ def video_gen(name_hash,name,text_content, image_urls,multiLang,avatar):
         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()
@@ -650,18 +662,9 @@ def video_gen(name_hash,name,text_content, image_urls,multiLang,avatar):
 
     tp = parser()
     img_dict_ls = tp.image_clip_info(sub_dict)
-    #if 'image_idx' in dic:
-    #            new_dic['image_obj'] = {'start':dic['start'],'idx':dic['image_idx']}
     img_clip_list = [None]*len(listdir(dir_photo+name_hash))
     img_list = [None]*len(img_clip_list)
-    #for p in listdir(dir_photo+name_hash):
-    #    p_idx = int(p)-1
-    #    img_list[p_idx] = openshot.FFmpegReader(dir_photo+name_hash+'/'+p)
-    #    img_list[p_idx].Open()
-    #    img_clip_list[p_idx] = video_photo_clip(vid=img_list[p_idx],layer=3
-    #            ,scale_x=0.81,scale_y=0.68,location_y=-0.03,position=img_dict_ls[int(p)+1]['start'],end=img_dict_ls[int(p)+1]['duration'],audio=False)
-    #    t.AddClip(img_clip_list[p_idx])
-    #    img_list[p_idx].Close()
+
     img_file_ls = listdir(dir_photo+name_hash)
     print(img_file_ls)
     print(img_dict_ls)
@@ -688,10 +691,22 @@ def video_gen(name_hash,name,text_content, image_urls,multiLang,avatar):
         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")
+
+    path = tmp_video_dir+name_hash+"script.txt"
+    f = open(path, 'r')
+    print(f.read())
+    f.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")
 
+    Ctr_Autosub.init()
+    Ctr_Autosub.generate_subtitles(tmp_video_dir+name_hash+".mp4",'zh',listener_progress,output=tmp_video_dir+name_hash+"script.txt",concurrency=DEFAULT_CONCURRENCY,subtitle_file_format=DEFAULT_SUBTITLE_FORMAT)
+    path = tmp_video_dir+name_hash+"script.txt"
+    f = open(path, 'r')
+    print(f.read())
+    f.close()
+
 
 def anchor_video_v2(name_hash,name,text_content, image_urls,multiLang,avatar):
     print(os.getcwd())
@@ -975,6 +990,7 @@ def anchor_video_eng(name_hash,name,text_content, image_urls,sub_titles,avatar):
     t.Close()
     w.Close()
     print("video at : www.choozmo.com:8168/"+video_sub_folder+name_hash+".mp4")
+    
     #line notifs
 
 import pyttsx3

二进制
api/__pycache__/gSlide.cpython-39.pyc


二进制
api/__pycache__/mailer.cpython-39.pyc


二进制
api/__pycache__/main.cpython-39.pyc


二进制
api/__pycache__/models.cpython-39.pyc


+ 40 - 12
api/gSlide.py

@@ -52,28 +52,56 @@ def parse_slide_url(slide_url,eng):
         
     for i, slide in enumerate(slides):
         # Check if the notes exists
+        print(slide['slideProperties']['notesPage']['pageElements'][1]['shape'].keys())
+        notes=''
         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("")
+            notes = slide['slideProperties']['notesPage']['pageElements'][0]['shape']['text']['textElements'][1]['textRun']['content']
+        if '[sub_title]' in notes:
+            sub_title = notes.split('[sub_title]')[1].strip()
+              
+            sub_title_list.append(sub_title)
+            notes = notes.split('[sub_title]')[0].strip()
+        
+        notes_list.append(notes)  
+
                 
         # 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']
-        
+
+def parse_slide_url(fileanme,img_upload_folder,img_url,eng):
+    
+    notes_list=[]
+    sub_title_list=[]
+    img_list=[]
+    
+    prs = Presentation(fileanme)
+    for slide in prs.slides:
+        notes_slide = slide.notes_slide
+        text_frame = notes_slide.notes_text_frame
+        print(text_frame.text)
+        shapes = slide.shapes
+        notes_list.append(text_frame.text)
+        for s in shapes:
+            img_name = str(time.time()).replace('.','')
+            image = s.image
+            image_bytes = image.blob
+            # ---make up a name for the file, e.g. 'image.jpg'---
+            image = Image.open(image_bytes)
+            image= image.convert("RGB")
+            image.save(img_upload_folder+img_name+'.jpg')
+            img_list.append(img_url+img_name+'.jpg')
+            #image_filename = ''
+            #with open(image_filename, 'wb') as f:
+            #    f.write(image_bytes)
+    return filename, notes_list, img_list

+ 75 - 32
api/main.py

@@ -58,25 +58,29 @@ app.add_middleware(
 )
 SECRET_KEY = "df2f77bd544240801a048bd4293afd8eeb7fff3cb7050e42c791db4b83ebadcd"
 ALGORITHM = "HS256"
-ACCESS_TOKEN_EXPIRE_MINUTES = 3000
+ACCESS_TOKEN_EXPIRE_DAYS = 5
 pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
 
 app.mount("/static", StaticFiles(directory="static"), name="static")
 app.mount("/static/img", StaticFiles(directory="static/img"), name="static/img")
-app.mount("/html", StaticFiles(directory="templates"), name="templates")
+app.mount("/templates", StaticFiles(directory="templates"), name="templates")
 
 templates = Jinja2Templates(directory="templates")
 oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
 
 tmp_video_dir = '../OpenshotService/tmp_video/'
 tmp_avatar_dir = '../../face_swap/tmp_avatar/'  #change source face path here
-
+resource_server = 'www.choozmo.com:8168/'
+resource_folder = '/var/www/html/'
 video_sub_folder = 'ai_anchor_video/'
 avatar_sub_folder = 'swap_save/'
 tmp_img_sub_folder = 'tmp_img/'
+pttx_sub_folder = 'tmp_pttx/'
 img_upload_folder = '/var/www/html/'+tmp_img_sub_folder
 video_dest = '/var/www/html/'+video_sub_folder
 avatar_dest = '/var/www/html/'+avatar_sub_folder
+pttx_dest = '/var/www/html/'+pttx_sub_folder
+
 
 
 # @app.get("/index2")
@@ -90,7 +94,6 @@ async def index2():
 # home page
 @app.get("/index", response_class=HTMLResponse)
 async def get_home_page(request: Request, response: Response):
-    return FileResponse('../html/index.html')
     return templates.TemplateResponse("index.html", {"request": request, "response": response})
 @app.get("/", response_class=HTMLResponse)
 async def get_home_page(request: Request, response: Response):
@@ -98,7 +101,6 @@ async def get_home_page(request: Request, response: Response):
 
 @app.get("/make_video", response_class=HTMLResponse)
 async def get_home_page(request: Request, response: Response, Authorize: AuthJWT = Depends()):
-    print('make_video'); # test
     try:
         Authorize.jwt_required()
     except Exception as e:
@@ -127,12 +129,18 @@ async def make_video_slide(request: Request, response: Response, Authorize: Auth
     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()):
+@app.post('/user_profile', response_class=HTMLResponse)
+async def user_profile(token: str = Depends(oauth2_scheme)):
     db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/AI_anchor?charset=utf8mb4')
-    Authorize.jwt_required()
-    current_user = Authorize.get_jwt_subject()
-    user_obj = first(db.query('SELECT * FROM users where username ="'+str(current_user)+'"'))
+    user_id = get_user_id(token)
+    user_obj = first(db.query('SELECT * FROM users where id ="'+str(user_id)+'"'))
+
+    if user_obj is None:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail="Missing token",
+            headers={"WWW-Authenticate": "Bearer"},
+        )
     video_num = str(first(db.query('SELECT COUNT(*) FROM history_input WHERE user_id ='+str(user_obj['id'])))['COUNT(*)'])
     total_sec = str(first(db.query('SELECT SUM(duration) FROM history_input where user_id='+str(user_obj['id'])))['SUM(duration)'])
     left_sec = user_obj['left_time']
@@ -141,7 +149,7 @@ def protected(request: Request, Authorize: AuthJWT = Depends()):
     statement = 'SELECT * FROM history_input WHERE user_id='+str(user_obj['id'])
     for row in db.query(statement):
         video_info_list.append({'id':row['id'],'title':row['name'],'duration':row['duration'],'url':row['url']})
-    dic_return = {'user_info':{'userName':current_user,'email':user_obj['email'],'video_num':video_num,'total_sec':total_sec,'left_sec':user_obj['left_time']},'video_info':video_info_list}
+    dic_return = {'user_info':{'userName':user_obj['username'],'email':user_obj['email'],'video_num':video_num,'total_sec':total_sec,'left_sec':user_obj['left_time']},'video_info':video_info_list}
     str_return = json.dumps(dic_return)
     return str_return
 
@@ -160,19 +168,20 @@ async def login_for_access_token(request: Request, form_data: OAuth2PasswordRequ
             detail="Incorrect username or password",
             headers={"WWW-Authenticate": "Bearer"},
         )
-    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
+    access_token_expires = timedelta(days=ACCESS_TOKEN_EXPIRE_DAYS)
     access_token = create_access_token(
         data={"sub": user.username}, expires_delta=access_token_expires
     )
     table = db['users']
     user.token = access_token
     table.update(dict(user), ['username'])
-    access_token = Authorize.create_access_token(subject=user.username)
-    refresh_token = Authorize.create_refresh_token(subject=user.username)
+    expires = timedelta(days=ACCESS_TOKEN_EXPIRE_DAYS)
+    access_token = Authorize.create_access_token(subject=user.username, expires_time=expires)
+    refresh_token = Authorize.create_refresh_token(subject=user.username, expires_time =expires)
     Authorize.set_access_cookies(access_token)
     Authorize.set_refresh_cookies(refresh_token)
     #return templates.TemplateResponse("index.html", {"request": request, "msg": 'Login'})
-    return {"access_token": access_token, "token_type": "bearer", "username": form_data.username}
+    return {"access_token": access_token, "token_type": "bearer"}
 
 
 @app.post("/token")
@@ -185,29 +194,44 @@ async def access_token(form_data: OAuth2PasswordRequestForm = Depends(), Authori
             detail="Incorrect username or password",
             headers={"WWW-Authenticate": "Bearer"},
         )
-    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
+    access_token_expires = timedelta(days=ACCESS_TOKEN_EXPIRE_DAYS)
     access_token = create_access_token(
         data={"sub": user.username}, expires_delta=access_token_expires
     )
     return {"access_token": access_token, "token_type": "bearer"}
 
-
-@app.post("/register")
-async def register(request: Request):
+#前後端分離完全實現後拔除
+@app.post("/register_old")
+async def register_old(request: Request):
+    db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/AI_anchor?charset=utf8mb4')
     user = models.User(**await request.form())
     user_obj = first(db.query('SELECT * FROM users where username ="'+str(user.username)+'"'))
-    if user_obj != None:
+    if user_obj == None:
         user_register(user)
-        return templates.TemplateResponse("login.html", {'request': request,"success": True}, status_code=status.HTTP_302_FOUND)
+        return '註冊成功! 請回到上頁登入帳號'
+        #return templates.TemplateResponse("make_video.html", {"request": request, "success": True},status_code=status.HTTP_302_FOUND)
+        #return templates.TemplateResponse("login.html", {'request': request,"success": True}, status_code=status.HTTP_302_FOUND)
     else:
         return {'msg':user.username+'重複,請更改'}
 
+@app.post("/register")
+async def register(request: models.register_req):
+    db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/AI_anchor?charset=utf8mb4')
+    user_obj = first(db.query('SELECT * FROM users where username ="'+str(request.username)+'"'))
+    if user_obj == None:
+        if user_register(request):
+            return {'msg':'ok'}
+        else:
+            return {'msg': '內部錯誤'}
+    else:
+        return {'msg':request.username+':使用者名稱重複,請更改'}
+
 
 @app.get('/logout')
 def logout(request: Request, Authorize: AuthJWT = Depends()):
     Authorize.jwt_required()
     Authorize.unset_jwt_cookies()
-    return {"msg": "Successfully logout"}
+    return {"msg": "ok"}
 
 @app.get("/gen_avatar")
 async def avatar():
@@ -226,7 +250,7 @@ async def swapFace(req:models.swap_req):
     
     x = threading.Thread(target=gen_avatar, args=(name_hash,req.imgurl))
     x.start()
-    return {'msg':'人物生成中,請稍候'}
+    return {'msg':'ok'}
 
 @app.post("/uploadfile/")
 async def create_upload_file(file: UploadFile = File(...)):
@@ -236,20 +260,39 @@ async def create_upload_file(file: UploadFile = File(...)):
             async with aiofiles.open(img_upload_folder+img_name+'.mp4', 'wb') as out_file:
                 content = await file.read()
                 await out_file.write(content) 
-            return {"msg": 'www.choozmo.com:8168/'+tmp_img_sub_folder+img_name+'.mp4'}
+            return {"msg": resource_server+tmp_img_sub_folder+img_name+'.mp4'}
         else:
             contents = await file.read()
             image = Image.open(io.BytesIO(contents))
             image= image.convert("RGB")
             image.save(img_upload_folder+img_name+'.jpg')
-            return {"msg": 'www.choozmo.com:8168/'+tmp_img_sub_folder+img_name+'.jpg'}
+            return {"msg": resource_server+tmp_img_sub_folder+img_name+'.jpg'}
     except Exception as e:
         logging.error(traceback.format_exc())
         return {'msg':'檔案無法使用'}
 
+@app.post("/upload_pttx/")
+async def upload_pttx(file: UploadFile = File(...)):
+    try:
+        if "_" in file.filename:
+            return {'msg':'檔案無法使用檔名不能含有"_"符號'}
+        else:
+            pttx_name = file.filename+'_'+str(time.time()).replace('.','')
+            with open(pttx_dest+pttx_name, "wb+") as file_object:
+                file_object.write(file.file.read())
+            return {"msg": resource_server+pttx_sub_folder+pttx_name}
+    except Exception as e:
+        logging.error(traceback.format_exc())
+        return {'msg':'檔案無法使用'}
+
+
 @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 req.url_type == 0:
+        name, text_content, image_urls = gSlide.parse_slide_url(req.slide_url,eng=False)
+    else :
+        filename = req.slide_url.replace(resource_server+pttx_sub_folder,resource_folder+pttx_sub_folder)
+        name, text_content, image_urls = gSlide.parse_pttx_url(filename,img_upload_folder,resource_server+tmp_img_sub_folder,eng=False)
     if len(image_urls) != len(text_content):
         return {'msg':'副標題數量、圖片(影片)數量以及台詞數量必須一致'}
     for idx in range(len(image_urls)):
@@ -431,6 +474,7 @@ def user_register(user):
     table = db['users']
     user.password = get_password_hash(user.password)
     table.insert(dict(user))
+    return True
 
 def get_password_hash(password):
     return pwd_context.hash(password)
@@ -445,12 +489,11 @@ def authenticate_user(username: str, password: str):
     if not verify_password(password, user.password):
         return False
     return user
-def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
+
+def create_access_token(data: dict, expires_delta):
     to_encode = data.copy()
-    if expires_delta:
-        expire = datetime.utcnow() + expires_delta
-    else:
-        expire = datetime.utcnow() + timedelta(minutes=15)
+    
+    expire = datetime.utcnow() + expires_delta
     to_encode.update({"exp": expire})
     encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
     return encoded_jwt
@@ -540,7 +583,7 @@ def gen_video_long_queue(name_hash,name,text_content, image_urls,avatar,multiLan
         except Exception as e:
             logging.error(traceback.format_exc())
             print('video generation error')
-            #notify_group('長影片錯誤-測試')
+            notify_group('長影片錯誤-測試')
         db['video_queue'].delete(id=top1['id'])
         db.query('UPDATE video_queue_status SET status = 0')
 

+ 8 - 1
api/models.py

@@ -30,6 +30,7 @@ class gSlide_req(BaseModel):
     avatar: str
     client_id :str
     multiLang :int
+    url_type: int
 
 class request_eng(BaseModel):
     name: str
@@ -55,4 +56,10 @@ class TokenData(BaseModel):
     username: Optional[str] = None
 
 class phone(BaseModel):
-    price: int
+    price: int
+
+class register_req(BaseModel):
+    username: str
+    email: str
+    password: str
+

+ 37 - 3
api/static/script_util.js

@@ -44,7 +44,41 @@ function openavatarModel() {
   avatarModal.show();
 }
 
-$('input[type=file]').on('change', prepareUpload);
+$('.pttx_uploader').on('change', prepareUploadPTTX);
+function prepareUploadPTTX(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: '/upload_pttx',
+    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('上傳檔案');
+      $('#url_type').val('1');
+    },
+    error: function (error) {
+      event.target.nextSibling.innerHTML = '';
+      event.target.nextSibling.textContent = '上傳檔案';
+      alert('圖片錯誤');
+    }
+  });
+}
+$('.img_uploader').on('change', prepareUpload);
+
 function prepareUpload(event) {
   files = event.target.files;
   var data = new FormData();
@@ -210,7 +244,7 @@ $("#send_slide").click(function () {
   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 }
+  dataOBJ = {'slide_url':$('#slide_raw_url').val(),"avatar": avatar,"multiLang":multiLang, "client_id": client_id,"url_type":$('#url_type').val() }
   objstr = JSON.stringify(dataOBJ);
   jwt_token =  get_jwt_token()
   var xhr = new XMLHttpRequest();
@@ -443,7 +477,7 @@ function renderimgBlock(i) {
   imguploadlabel.classList.add('upload-btn');
   imguploadlabel.textContent = '上傳檔案';
   imgInputs.appendChild(imguploadlabel);
-  $('input[type=file]').on('change', prepareUpload);
+  $('.img_uploader').on('change', prepareUpload);
 }
 
 

+ 2 - 4
api/templates/index.html

@@ -31,7 +31,7 @@
     <!-- navbar -->
     <nav class="navbar navbar-expand-lg navbar-light">
         <div class="container-fluid">
-            <a class="navbar-brand" href="./index">AI Spokesgirl</a>
+            <a class="navbar-brand" href="/index">AI Spokesgirl</a>
             <button class="navbar-toggler" type="button" data-bs-toggle="collapse"
                 data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false"
                 aria-label="Toggle navigation">
@@ -45,9 +45,7 @@
                     <li class="nav-item">
                         <a class="nav-link active btn-gocreate text-white" aria-current="page" href="/make_video_slide" set-lan="html:make_slides">SLIDE製作影片</a>                        
                     </li>
-                    <li class="nav-item">
-                        <a class="nav-link active btn-gocreate text-white" aria-current="page" href="/make_video_long" set-lan="html:make_slides">製作長影片</a>                        
-                    </li>
+
                 </ul>
                 
                 <ul class="navbar-nav mb-2 mb-lg-0">

+ 1 - 1
api/templates/login.html

@@ -60,7 +60,7 @@
 
                 </div>
                 <div class="tab-pane fade p-lg-3" id="register" role="tabpanel" aria-labelledby="register-tab">
-                    <form method="post" action="register">
+                    <form method="post" action="register_old">
                         <div class="form-floating mb-3">
                             <input type="text" class="form-control" id="username" name="username" placeholder="User name">
                             <label for="username"><i class="fas fa-user me-2"></i>User name</label>

+ 1 - 1
api/templates/make_video.html

@@ -12,7 +12,7 @@
     <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 Spokesgirl</a></h2>
+        <h2 class="go_title" href="/index"><a class="nav-link active" aria-current="page" href="/index">AI Spokesgirl</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><lan set-lan="html:usage_intro">使用說明</lan></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><lan set-lan="html:history">歷史紀錄</lan></li>

+ 7 - 77
api/templates/make_video_long.html

@@ -1,80 +1,10 @@
 
-<!DOCTYPE html>
-<html lang="zh-TW">
-
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>AI Spokesgirl</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/owl.carousel.min.css">
-    <link rel="stylesheet" href="static/owl.theme.default.min.css">
-    <link rel="stylesheet" href="static/style.css">
-    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
-    <title>AI Spokesgirl</title>
-</head>
-
-<body>
-    <!-- ================================================================= -->
-    <!-- navbar -->
-    <nav class="navbar navbar-expand-lg navbar-light">
-        <div class="container-fluid">
-            <a class="navbar-brand" href="./index">AI Spokesgirl</a>
-            <button class="navbar-toggler" type="button" data-bs-toggle="collapse"
-                data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false"
-                aria-label="Toggle navigation">
-                <span class="navbar-toggler-icon"></span>
-            </button>
-            <div class="collapse navbar-collapse" id="navbarSupportedContent">
-                <ul class="navbar-nav me-auto mb-2 mb-lg-0">
-                    <li class="nav-item">
-                        <a class="nav-link active btn-gocreate text-white me-2" aria-current="page" href="./make_video" set-lan="html:make_video">製作影片</a>
-                    </li>
-                    <li class="nav-item">
-                        <a class="nav-link active btn-gocreate text-white" aria-current="page" href="./make_video_slide" set-lan="html:make_slides">SLIDE製作影片</a>                        
-                    </li>
-                    <li class="nav-item">
-                        <a class="nav-link active btn-gocreate text-white" aria-current="page" href="./make_video_long" set-lan="html:make_slides">製作長影片</a>                        
-                    </li>
-                </ul>
-                
-                <ul class="navbar-nav mb-2 mb-lg-0">
-                    <li class="nav-item dropdown">
-                        <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
-                            中/En
-                        </a>
-                        <ul class="dropdown-menu" aria-labelledby="navbarDropdown">
-                            <li><button class="nav-link lan-swtich" aria-current="page" set-lan="html:en" onclick="changeLan(this)" value="en">English</button></li>
-                            <li><button class="nav-link lan-switch" aria-current="page" set-lan="html:zh" onclick="changeLan(this)" value="zh">中文</button></li>
-                        </ul>
-                    </li>
-                    <li class="nav-item">
-                        <a class="nav-link active" aria-current="page" href="./login" set-lan="html:login">登入</a>
-                    </li>
-                    <li class="nav-item">
-                        <a class="nav-link" aria-current="page" href="./user_profile" set-lan="html:user_profile">會員資料</a>
-                    </li>
-                    <li class="nav-item">
-                        <a class="nav-link" aria-current="page" href="./logout" set-lan="html:logout">登出</a>
-                    </li>
-                </ul>
-            </div>
-        </div>
-    </nav>
-    <!-- ================================================================= -->
-
-
-    <!-- ================================================================= -->
+ {% extends "index.html" %}
+{% block title %}Login{% endblock %}
+{% block head %}
+{{ super() }}
+{% endblock %}
+{% block content %}
 
 
     <!-- content -->
@@ -82,7 +12,7 @@
     <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 Spokesgirl</a></h2>
+        <h2 class="go_title" href="/index"><a class="nav-link active" aria-current="page" href="/index">AI Spokesgirl</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><lan set-lan="html:usage_intro">使用說明</lan></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><lan set-lan="html:history">歷史紀錄</lan></li>

+ 4 - 2
api/templates/make_video_slide.html

@@ -10,7 +10,7 @@
   <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>
+      <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>
@@ -29,7 +29,9 @@
         <!-- 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 id=slide_raw_url type="text" name='t1' class='title_new' value="" placeholder="連結" /><input id="img3" type="file" class="pttx_uploader">
+          <input id ='url_type' type='hidden' value='0'>
+          <label for="myCheck">加入英文:</label> 
           <input type="checkbox" id="multiLang" > <br/>
         </fieldset>
         <fieldset>

+ 1 - 1
api/templates/script_index.js

@@ -15,7 +15,7 @@ function loginByEnter(e) {
 
 function login(){
   console.log('login!');
-  var url = "http://www.choozmo.com:8887/";
+  var url = "login";
   var xhr = new XMLHttpRequest();
   xhr.open("POST", url);
   xhr.setRequestHeader("accept", "application/json");

二进制
api/util/__pycache__/swap_face.cpython-39.pyc


+ 29 - 9
api/util/user.py

@@ -1,6 +1,5 @@
 class user_util():
-
-    def get_user_id(token):
+    def get_user_id(self, token):
         db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/AI_anchor?charset=utf8mb4')
         credentials_exception = HTTPException(
             status_code=status.HTTP_401_UNAUTHORIZED,
@@ -21,14 +20,14 @@ class user_util():
         user_id = first(db.query('SELECT * FROM users where username="' + user.username+'"'))['id']
         return user_id
 
-    def check_user_exists(username):
+    def check_user_exists(self, username):
         db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/AI_anchor?charset=utf8mb4')
         if int(next(iter(db.query('SELECT COUNT(*) FROM AI_anchor.users WHERE username = "'+username+'"')))['COUNT(*)']) > 0:
             return True
         else:
             return False
 
-    def get_user(username: str):
+    def get_user(self, username: str):
         db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/AI_anchor?charset=utf8mb4')
         if not check_user_exists(username):  # if user don't exist
             return False
@@ -37,17 +36,17 @@ class user_util():
         user = models.User(**user_dict)
         return user
         
-    def user_register(user):
+    def user_register(self, user):
         db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/AI_anchor?charset=utf8mb4')
         table = db['users']
         user.password = get_password_hash(user.password)
         table.insert(dict(user))
 
-    def get_password_hash(password):
+    def get_password_hash(self, password):
         return pwd_context.hash(password)
-    def verify_password(plain_password, hashed_password):
+    def verify_password(self, plain_password, hashed_password):
         return pwd_context.verify(plain_password, hashed_password)
-    def authenticate_user(username: str, password: str):
+    def authenticate_user(self, username: str, password: str):
         db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/AI_anchor?charset=utf8mb4')
         if not check_user_exists(username):  # if user don't exist
             return False
@@ -55,4 +54,25 @@ class user_util():
         user = models.User(**user_dict)
         if not verify_password(password, user.password):
             return False
-        return user
+        return user
+
+    def get_roles(self, username):
+        db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/AI_anchor?charset=utf8mb4')
+        state = 'SELECT * FROM user_role '\
+        'INNER JOIN users on user_role.user_id= users.id'\
+        'INNER JOIN role on user_role.role_id = role.id '\
+        'WHERE username=username'
+        role_list = []
+        for row in db.query(statement):
+            role_list.append({'id':row['role_id'],'name':row['name']})
+        return role_list
+        
+    def add_role(self, username,role_id):
+        db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/AI_anchor?charset=utf8mb4')
+        user_role_table = db['user_role']
+        user_role_table.insert({'user_id':,'role_id':role_id})
+
+    def get_user_id(self, username):
+        db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/AI_anchor?charset=utf8mb4')
+        return str(first(db.query('SELECT COUNT(*) FROM history_input WHERE user_id ='+str(user_obj['id'])))['COUNT(*)'])
+