Parcourir la source

Merge branch 'master' of http://git.choozmo.com:3000/choozmo/AI_Spokesgirl

huai-sian il y a 3 ans
Parent
commit
57eca32246

+ 161 - 0
OpenshotService/hakkaUtil.py

@@ -0,0 +1,161 @@
+# !pip install librosa==0.7.2
+# !pip install numba==0.48.0
+# !pip install pydub
+# !sudo yum update
+# !sudo yum install epel-release
+# !sudo rpm --import http://li.nux.ro/download/nux/RPM-GPG-KEY-nux.ro
+# !sudo rpm -Uvh http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0-5.el7.nux.noarch.rpm
+# !sudo yum install ffmpeg ffmpeg-devel -y
+
+import pandas as pd
+import numpy as np
+import re
+import jieba
+import jieba.posseg as pseg
+import urllib
+import urllib.request
+import librosa
+from pydub import AudioSegment
+from pydub.silence import split_on_silence
+import itertools
+def import_data():
+  drop_id = [14,4806,5024]
+  word_data = pd.DataFrame()
+  for i in ['si3-1','si3-2','siw']:
+    tmp_word_data = pd.read_csv('csv_imtong/{}.csv'.format(i))
+    gender_list = []
+    tmp_word_data_class = list(tmp_word_data.分類.unique())
+    for i in range(len(tmp_word_data_class)):
+      if i%2 == 0:
+        gender_list += ['Male']
+      else:
+        gender_list += ['Female']
+    gender_df = pd.DataFrame({'分類':tmp_word_data_class,'gender':gender_list})
+    tmp_word_data = pd.merge(tmp_word_data,gender_df,on='分類')
+    word_data = word_data.append(tmp_word_data)
+  word_data = word_data.reset_index(drop=True)
+  word_data = word_data.loc[:,['客家語','客語標音','客語音檔','華語詞義','gender']]
+  # 丟掉有問題音檔
+  word_data = word_data.drop(drop_id,axis=0).reset_index(drop=True)
+  # 整理資料,產生華語詞義集
+  # 以下是要被取代的字眼
+  repleace_list = ['引申為','...','或形容','…','形容','?','例如:','亦可說','「','」','例如','猶如華語中的','相當於華語的']
+  ch_word_list = []
+  for i in word_data.華語詞義:
+    i_ = i
+    for repleace_word in repleace_list:
+      i_ = i_.replace(repleace_word,'')
+    tmp_ch_word = re.sub(r'[,、;]','/',i_)
+    if tmp_ch_word.find('。')>=0:
+      tmp_ch_word = re.sub('\。\S+','',tmp_ch_word).replace('。','')
+    if tmp_ch_word.find('(')>=0:
+      tmp_ch_word = re.sub('\(\S+\)','',tmp_ch_word)
+    ch_word_list.append(tmp_ch_word.split('/'))
+  word_data['華語詞義集'] = ch_word_list
+  # 找出重複的音檔
+  multi_sound = word_data.loc[[i.find('(')>=0 or i.find('【')>=0 or i.find('/')>=0  for i in word_data.客語標音],:]
+  return word_data,multi_sound
+
+# 下載客語單詞
+def download_mp3(word_data,multi_sound):
+  print('Run download_mp3')
+  for j in range(len(word_data)):
+    if j%500==0:
+      print(j,'/',len(word_data))
+    urllib.request.urlretrieve(word_data.loc[j,:]['客語音檔'], "mp3/{}.mp3".format(j))
+  #刪除一些重複地念法到mp3_uni
+  print('Generate unin mp3')
+  for i in list(multi_sound.index):
+    sound = AudioSegment.from_mp3("mp3/{}.mp3".format(i))
+    loudness = sound.dBFS
+    chunks = split_on_silence(sound,
+        # must be silent for at least half a second,沉默半秒
+        min_silence_len=200,
+        # consider it silent if quieter than -16 dBFS
+        silence_thresh=-50,
+        keep_silence=100
+    )
+    if len(chunks)==1:
+      print(i)
+    chunks[0].export("mp3_uni/{}.mp3".format(i), format="mp3")
+
+def import_hakka_100():
+  tmp_word_100 = pd.read_csv('csv_imtong/hakka100.csv')
+  chinese_clean_list = []
+  for i in tmp_word_100.chinese:
+    chinese_clean = i[:-1].replace('。','').replace('?','').replace('!','/').replace(',','/').replace('、','/')
+    if chinese_clean.find('(')>=0:
+      chinese_clean = re.sub('\(\S+\)','',chinese_clean)
+
+    chinese_clean_list += [chinese_clean.split('/')]
+  tmp_word_100['chinese_clean'] = chinese_clean_list
+  hakka_100 = tmp_word_100.explode('chinese_clean').reset_index(drop=True)
+  return hakka_100
+
+def download_hakka_100(hakka_100):
+  #下載客語100句
+  print('Run download_hakka_100')
+  for m in range(len(hakka_100)):
+
+    print(m,'/',len(hakka_100))
+    urllib.request.urlretrieve(hakka_100.loc[m,:]['url '], "mp3/hakka_100_{}.mp3".format(j))
+  #刪除一些重複地念法到mp3_uni
+  print('Generate unin hakka_100 mp3')
+  j = 0
+  for i in list(hakka_100.index):
+    sound = AudioSegment.from_mp3("mp3/hakka_100_{}.mp3".format(i))
+    loudness = sound.dBFS
+    chunks = split_on_silence(sound,
+        # must be silent for at least half a second,沉默半秒
+        min_silence_len=300,
+        # consider it silent if quieter than -16 dBFS
+        silence_thresh=-45,
+        keep_silence=400
+    )
+    if len(hakka_100.loc[i,:]['chinese_clean'])==len(chunks):
+      for k in chunks:
+        k.export("mp3_uni/hakka_100_{}.mp3".format(j), format="mp3")
+        j += 1
+    else:
+      # 印出錯誤的音檔
+      print('Error',i,chunks)
+
+def import_jieba_userdict(ch_word_list, userDict_path='userDict.txt'):
+  f = open(userDict_path, 'w',encoding="utf-8")
+  for i in range(len(ch_word_list)):
+    f.write(ch_word_list[i]+'\n')
+  f.close()
+  jieba.load_userdict(userDict_path)
+  return jieba
+
+def gen_hakka_tts(word_data,multi_sound,hakka_100,ch_sentence,gender,mp3_path='test1.mp3',verbose=0):
+  Y = []
+  pitch_step = 0
+  if gender == 0:
+    pitch_step=8
+  else:
+    pitch_step=0
+  print(jieba.lcut(ch_sentence))
+  for word in jieba.lcut(ch_sentence):
+    tmp_mapping_100 = hakka_100.loc[[word == i for i in hakka_100.chinese_clean],:].head(1)
+    if tmp_mapping_100.empty:
+      tmp_mapping = word_data.loc[[word in i for i in word_data.華語詞義集],:].head(1)
+      if verbose==1:print(tmp_mapping)
+      if tmp_mapping.empty:
+        if verbose==1:print('no mach',word)
+        pass
+      else:
+        if tmp_mapping.index[0] in list(multi_sound.index):
+          y, sr = librosa.load('mp3_uni/{}.mp3'.format(tmp_mapping.index[0]))
+          if tmp_mapping.gender.values[0] == 'Male':
+            y = librosa.effects.pitch_shift(y, sr, n_steps=pitch_step)
+        else:
+          y, sr = librosa.load('mp3/{}.mp3'.format(tmp_mapping.index[0]))
+          if tmp_mapping.gender.values[0] == 'Male':
+            y = librosa.effects.pitch_shift(y, sr, n_steps=pitch_step)
+        Y += list(y[abs(y)>0.0005])
+    else:
+      y, sr = librosa.load('mp3_uni/hakka_100_{}.mp3'.format(tmp_mapping_100.index[0]))
+      y = librosa.effects.pitch_shift(y, sr, n_steps=pitch_step)
+      Y += list(y[abs(y)>0.0005])
+  librosa.output.write_wav(mp3_path, np.array(Y), sr)

+ 0 - 0
OpenshotService/util/__pycache__/parser.cpython-38.pyc → OpenshotService/openUtil/__pycache__/parser.cpython-38.pyc


+ 0 - 0
OpenshotService/util/__pycache__/parser.cpython-39.pyc → OpenshotService/openUtil/__pycache__/parser.cpython-39.pyc


+ 0 - 0
OpenshotService/util/parser.py → OpenshotService/openUtil/parser.py


+ 45 - 18
OpenshotService/openshot_video_generator.py

@@ -29,7 +29,17 @@ from pytranscriber.control.ctr_autosub import Ctr_Autosub
 import multiprocessing
 from itertools import groupby
 from operator import itemgetter
-from util.parser import parser
+from openUtil.parser import parser
+import pandas as pd
+import numpy as np
+import jieba
+import jieba.posseg as pseg
+import urllib.request
+import librosa
+from pydub import AudioSegment
+from pydub.silence import split_on_silence
+import itertools
+from hakkaUtil import *
 
 dir_sound = 'mp3_track/'
 dir_photo = 'photo/'
@@ -125,6 +135,18 @@ def make_dir(name_hash):
     except FileExistsError:
         print("~~~~~~Warning~~~~~~~~~Directory " , dir_subtitle+name_hash ,  " already exists")
 
+def hakkaTTS(mp3_path,ch_sentence,gender):
+    download = False #如果要下載才需要Ture
+    hakka_100 = import_hakka_100()
+    word_data,multi_sound = import_data()
+    if download:
+        download_mp3(word_data,multi_sound)
+        download_hakka_100(hakka_100)
+    ch_word_list = list(itertools.chain(*word_data['華語詞義集'].tolist())) + hakka_100.chinese_clean.tolist()
+    import_jieba_userdict(ch_word_list=ch_word_list, userDict_path='userDict.txt')
+    gen_hakka_tts(word_data,multi_sound,hakka_100,ch_sentence,gender,mp3_path)
+
+
 def file_prepare(name, name_hash,text_content,image_urls,multiLang,lang='zh'):
     make_dir(name_hash)
     img_num = 1
@@ -152,22 +174,27 @@ def file_prepare(name, name_hash,text_content,image_urls,multiLang,lang='zh'):
     #make mp3
     txt_idx = 0
     for txt in text_content:
-        if lang!='zh' or multiLang==1:
-            if lang!='zh':
-                tts = gTTS(txt)
-                tts.save(dir_sound+name_hash+"/"+str(txt_idx)+"raw.mp3")
-            else:
-                tts = gTTS(txt,lang='zh-tw')
-                tts.save(dir_sound+name_hash+"/"+str(txt_idx)+"raw.mp3")
-            #speed up 
-            ff = ffmpy.FFmpeg(inputs={dir_sound+name_hash+"/"+str(txt_idx)+"raw.mp3": None}
-                            , outputs={dir_sound+name_hash+"/"+str(txt_idx)+".mp3": ["-filter:a", "atempo=1.2"]})
-            ff.run()
-            os.remove(dir_sound+name_hash+"/"+str(txt_idx)+"raw.mp3")
+        if multiLang==3:
+            hakkaTTS(dir_sound+name_hash+"/"+str(txt_idx)+".mp3",txt,0)
+        elif multiLang==4:
+            hakkaTTS(dir_sound+name_hash+"/"+str(txt_idx)+".mp3",txt,1)
         else:
-            print('use zhtts')
-            tts = zhtts.TTS() 
-            tts.text2wav(txt,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)+"raw.mp3")
+                else:
+                    tts = gTTS(txt,lang='zh-tw')
+                    tts.save(dir_sound+name_hash+"/"+str(txt_idx)+"raw.mp3")
+                #speed up 
+                ff = ffmpy.FFmpeg(inputs={dir_sound+name_hash+"/"+str(txt_idx)+"raw.mp3": None}
+                                , outputs={dir_sound+name_hash+"/"+str(txt_idx)+".mp3": ["-filter:a", "atempo=1.2"]})
+                ff.run()
+                os.remove(dir_sound+name_hash+"/"+str(txt_idx)+"raw.mp3")
+            else:
+                print('use zhtts')
+                tts = zhtts.TTS() 
+                tts.text2wav(txt,dir_sound+name_hash+"/"+str(txt_idx)+".mp3")
         txt_idx+=1
     print("mp3 file made")
     #make title as image
@@ -874,7 +901,7 @@ def anchor_video_v2(name_hash,name,text_content, image_urls,multiLang,avatar,fre
         wm = openshot.QtImageReader(dir_video+"freeTrialWatermark.png")
         wm.Open()     
         wm_clip = video_photo_clip(wm,layer=6,position=0,end=int(head_duration+main_timer+ED_duration))
-        t.AddClip(wm_clip)
+        #t.AddClip(wm_clip)
         
     else:
         print("THIS IS NOT TRIAL")
@@ -1023,7 +1050,7 @@ def anchor_video_eng(name_hash,name,text_content, image_urls,sub_titles,avatar,f
         wm = openshot.QtImageReader(dir_video+"freeTrialWatermark.png")
         wm.Open()     
         wm_clip = video_photo_clip(wm,layer=6,position=0,end=int(head_duration+main_timer+ED_duration))
-        t.AddClip(wm_clip)
+        #t.AddClip(wm_clip)
         print("THIS IS TRIAL")
     else:
         print("THIS IS NOT TRIAL")

+ 21 - 3
api/routers/toolAvatarVoiceOnly.py

@@ -2,11 +2,10 @@ from fastapi import APIRouter
 from pydantic import BaseModel
 from gtts import gTTS
 import ffmpy
-
 import random
 from typing import Optional
 from fastapi.staticfiles import StaticFiles
-from fastapi import FastAPI,File,Request,Response
+from fastapi import FastAPI,File,Request,Response,UploadFile
 import util,os, math, time
 from pydantic import BaseModel
 from fastapi.templating import Jinja2Templates
@@ -27,6 +26,25 @@ dir_sound = ''
 dir_anchor = ''
 tool_dest = '/var/www/html/tools/'
 
+
+@router.post("/genAvatar_uploadmp3/", tags=["tools"])
+async def genAvatar_uploadmp3(file: UploadFile = File(...)):
+    name_hash = str(time.time()).replace('.','')
+
+    with open(dir_sound+name_hash+".mp3", "wb+") as file_object:
+        file_object.write(file.file.read())
+    
+    
+    x = threading.Thread(target=makeAvatar, args=(str(name_hash),99))
+    x.start()
+    return {'msg':'ok'}
+
+def makeAvatar(name_hash,avatar):
+    call_anchor(name_hash,avatar)
+    shutil.copy(dir_anchor+name_hash+".mp4",tool_dest+name_hash+'.mp4')
+    notify_choozmo('avatar at www.choozmo.com:8168/tools/'+name_hash+'.mp4')
+    os.remove(dir_sound+name_hash+".mp3")
+
 @router.post("/get_material/", tags=["tools"])
 async def get_material(text_in: text_in):
     x = threading.Thread(target=memberOfQueue, args=(text_in.text,text_in.lang,text_in.avatar))
@@ -35,6 +53,7 @@ async def get_material(text_in: text_in):
 
 def memberOfQueue(txt,lang,avatar):
     q.put(compose(txt,lang,avatar))
+    
 
 def compose(txt,lang,avatar):
     name_hash = str(time.time()).replace('.','')
@@ -85,7 +104,6 @@ def call_anchor(name_hash,avatar):
         if ros.path.exists('/tmp/results/'+str(val)):
             break
         time.sleep(5)
-        
     fr=conn.builtins.open('/tmp/results/'+str(val)+'.mp4','rb')
     fw=open(dir_anchor+name_hash+".mp4",'wb')
     while True:

BIN
api/static/img/17.webp


BIN
api/static/img/18.webp


BIN
api/static/img/19.webp


+ 6 - 1
api/static/script_util.js

@@ -140,6 +140,11 @@ $(".next").click(function () {
   }
   multiLang = 0
   if ($('#multiLang').prop("checked")) {multiLang = 1;}
+  if ($('#hakka_in').prop("checked")) {multiLang=3;}
+  if(avatar==99)
+  {
+    multiLang=4;
+  }
   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)
@@ -153,7 +158,7 @@ $(".next").click(function () {
     $(".next").prop("disabled",false)
     if (xhr.readyState === 4) {
       responseOBJ = JSON.parse(xhr.responseText)
-      if (responseOBJ.msg=='ok')
+      if (true)
       {
         Swal.fire({
           title: "資料已送出",

+ 28 - 0
api/templates/make_video.html

@@ -36,6 +36,10 @@
             <h3  class="fs-subtitle"><lan set-lan="html:choose_character">選擇人物</lan><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 set-lan="html:p_choose_character" value="請選擇人物" selected="selected" disabled>>請選擇人物</option>              
+              <option value="17">客語人員1</option>
+              <option value="18">客語人員2</option>
+              <option value="19">客語人員3</option>
+              <option value="99">Jared</option>
               <option value="7">Peggy</option>
               <option value="8">Stacy</option>
               <option value="10">Nina黑</option>
@@ -45,6 +49,28 @@
               <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/17.webp" class="card-img-top" alt="..."></div>
+                <div class="card-body">
+                  <h5 class="card-title">客語人員1</h5>
+                </div>
+              </div><div class="card item" data-avatar="Peggy" data-img="peggy">
+                <div class="imgfr"><img src="static/img/18.webp" class="card-img-top" alt="..."></div>
+                <div class="card-body">
+                  <h5 class="card-title">客語人員2</h5>
+                </div>
+              </div><div class="card item" data-avatar="Peggy" data-img="peggy">
+                <div class="imgfr"><img src="static/img/19.webp" class="card-img-top" alt="..."></div>
+                <div class="card-body">
+                  <h5 class="card-title">客語人員3</h5>
+                </div>
+              </div>
+              <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">Jared</h5>
+                </div>
+              </div>
               <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">
@@ -93,6 +119,8 @@
             <h3 class="fs-subtitle" set-lan="html:lines">台詞</h3>
  <label for="myCheck" set-lan="html:add_eng">加入英文:</label> 
             <input type="checkbox" id="multiLang" > <br/>
+  <label for="myCheck" set-lan="html:add_eng">客語</label> 
+            <input type="checkbox" id="hakka_in" > <br/>
              <div class="subtitle-inputs">
             
             </div>