台語tts.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. import os
  2. # import requests
  3. # from bs4 import BeautifulSoup
  4. from selenium.webdriver.common.by import By
  5. # import re
  6. import time
  7. # from fake_useragent import UserAgent
  8. import undetected_chromedriver as uc
  9. from datetime import datetime
  10. import random
  11. import string
  12. # from selenium.webdriver.common.action_chains import ActionChains
  13. # from selenium.webdriver.common.keys import Keys
  14. # from selenium.webdriver.support.ui import WebDriverWait
  15. # from selenium.webdriver.support import expected_conditions as EC
  16. # from ga4mp import FirebaseMP
  17. from dotenv import load_dotenv
  18. import os
  19. import shutil
  20. import logging
  21. from fastapi import APIRouter, FastAPI
  22. import uvicorn
  23. from fastapi.middleware.cors import CORSMiddleware
  24. from supabase import create_client, Client
  25. load_dotenv()
  26. logging.basicConfig(level=logging.INFO)
  27. SUPABASE_URL: str = os.environ.get('SUPABASE_URL')
  28. SUPABASE_KEY: str = os.environ.get('SUPABASE_KEY')
  29. supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
  30. def download_blob(browser, blob_url, filename='output.mp3'):
  31. # 使用 Selenium 獲取 Blob 內容
  32. js_code = f"""
  33. fetch('{blob_url}')
  34. .then(response => response.blob())
  35. .then(blob => {{
  36. const url = URL.createObjectURL(blob);
  37. const a = document.createElement('a');
  38. a.href = url;
  39. a.download = '{filename}';
  40. document.body.appendChild(a);
  41. a.click();
  42. document.body.removeChild(a);
  43. }})
  44. .catch(error => console.error('Error downloading file:', error));
  45. """
  46. browser.execute_script(js_code)
  47. def tts_downloadfile(text):
  48. start_time = time.time()
  49. print(f'text長度: {len(text)}')
  50. num = random.randint(3,5)
  51. url = 'http://tts001.iptcloud.net:8804/'
  52. default_download_folder = os.path.join(os.path.expanduser('~'), 'Downloads')
  53. download_folder = '/var/www/html/innolux/tts_folder'
  54. file_name = datetime.now().strftime(f"%Y%m%d%H%M%S_{''.join(random.sample(string.ascii_lowercase, 3))}.mp3")
  55. # 替換為你希望的文件夾路徑
  56. os.makedirs(download_folder, exist_ok=True)
  57. options = uc.ChromeOptions()
  58. options.add_argument('--ignore-certificate-errors')
  59. prefs = {
  60. "download.default_directory": download_folder, # 設定預設下載文件夾
  61. "download.prompt_for_download": False,
  62. "safebrowsing.enabled": True, # 確保安全瀏覽
  63. }
  64. options.add_experimental_option("prefs", prefs)
  65. # options.add_argument('--incognito')
  66. options.add_argument('--headless') # 如果不想顯示瀏覽器可以啟用這行
  67. options.add_argument("--disable-gpu") # 禁用 GPU 加速
  68. # 設置自定義 headers
  69. with uc.Chrome(options=options, version_main=129) as browser:
  70. try:
  71. browser.get(url)
  72. time.sleep(num)
  73. # 轉中文成台語拼音
  74. browser.find_element(By.XPATH, '//*[@id="js-input"]').send_keys(text)
  75. time.sleep(0.1)
  76. browser.find_element(By.XPATH, '//*[@id="js-translate"]').click()
  77. time.sleep(0.1 + len(text)*0.01)
  78. browser.execute_script('window.scrollBy(0, 200);')
  79. # 轉語音
  80. browser.find_element(By.XPATH, '//*[@id="button1"]').click()
  81. # time.sleep(len(text)*0.6)
  82. # audio_element = browser.find_element(By.XPATH, '//*[@id="audio1"]')
  83. # time.sleep(0.2)
  84. # # 取得 <audio> 標籤的屬性(例如 src)
  85. audio_src = None
  86. while not audio_src:
  87. if time.time() - start_time > 45:
  88. return 'Time exceeded'
  89. audio_element = browser.find_element(By.XPATH, '//*[@id="audio1"]')
  90. audio_src = audio_element.get_attribute('src')
  91. if audio_src:
  92. print("音頻來源:", audio_src)
  93. download_blob(browser, audio_src, file_name)
  94. else:
  95. print("尚未檢測到音頻,繼續等待...")
  96. time.sleep(0.3) # 每隔 0.3 秒檢測一次
  97. # 下載音檔
  98. # download_blob(browser, audio_src, file_name)
  99. is_default = False
  100. # check 是否下載完成
  101. file_path = '/var/www/html/innolux/tts_folder' + '/' + file_name
  102. default_file_path = '/root/Downloads' + '/' + file_name
  103. while not os.path.exists(file_path):
  104. print('...')
  105. # if default_file_path:
  106. # is_default = True
  107. # break
  108. time.sleep(0.001)
  109. # destination_path = os.path.join(download_folder, datetime.now().strftime(f"%Y%m%d%H%M%S_{''.join(random.sample(string.ascii_lowercase, 3))}.wav"))
  110. # shutil.move(file_path, destination_path)
  111. # if is_default:
  112. # while not os.path.exists(default_file_path):
  113. # print('...')
  114. # time.sleep(0.001)
  115. # shutil.move(default_file_path, file_path)
  116. # print("檔案移動完成")
  117. print(f"下載完成: {file_path}")
  118. file_path = file_path.split('html/')[1]
  119. print(file_path)
  120. print(time.time() - start_time)
  121. return file_path
  122. except Exception as e:
  123. print(f'Error: {e}')
  124. return e
  125. app = FastAPI()
  126. app.add_middleware(
  127. CORSMiddleware,
  128. allow_origins=["*"],
  129. allow_credentials=True,
  130. allow_methods=["*"],
  131. allow_headers=["*"],
  132. )
  133. @app.post('/tts')
  134. async def tts(answer: str):
  135. file_path = tts_downloadfile(answer)
  136. # if '.mp3' not in file_path:
  137. # return {"message": file_path}
  138. if file_path:
  139. return {'message': {'mp3_url': file_path}}
  140. else:
  141. return {"message": "tts processing failed."}
  142. # from apscheduler.schedulers.background import BackgroundScheduler
  143. # import asyncio
  144. # import requests
  145. # scheduler = BackgroundScheduler()
  146. # loop = asyncio.get_event_loop()
  147. # # 巴巴群組
  148. # def notify_line(id, question, message):
  149. # # url = 'http://cmm.ai:3001/api/push/PLSDC1vOG9'
  150. # url = 'https://notify-api.line.me/api/notify'
  151. # token = 'OtAC4mBxi1tHjFT5RDcCiA8NwNKuxHVOAlZU5iO04XB' # 巴巴工程師群組
  152. # headers = {
  153. # 'Authorization': 'Bearer ' + token
  154. # }
  155. # # 構造請求的數據
  156. # data = {
  157. # 'message': f"\n群創\nid:【{id}】\n問題: {question}\nmessage: {message}"
  158. # }
  159. # try:
  160. # response = requests.post(url, headers=headers, data=data)
  161. # if response.status_code == 200:
  162. # print("Notification sent successfully.")
  163. # else:
  164. # print(f"Failed to send notification. Status code: {response.status_code}")
  165. # except Exception as e:
  166. # print(f"An error occurred: {e}")
  167. # def sub_1_minute():
  168. # time.sleep(1)
  169. # cursor = supabase.table('INNOLUX_cache').select('*').filter('mp3_url', 'is', 'null').order('id', desc=False).execute()
  170. # if cursor.data:
  171. # data = cursor.data[0]
  172. # if data['is_run']:
  173. # return
  174. # try:
  175. # supabase.table('INNOLUX_cache').update({'is_run':True}).eq('question', data['question']).eq('answer', data['answer']).execute()
  176. # file_path = tts_downloadfile(data['answer'])
  177. # if '.mp3' in file_path:
  178. # supabase.table('INNOLUX_cache').update({'mp3_url':file_path}).eq('question', data['question']).eq('answer', data['answer']).eq('is_run', True).execute()
  179. # print(f'{file_path} 已存入資料庫')
  180. # supabase.table('INNOLUX_cache').update({'is_run':None}).eq('question', data['question']).eq('answer', data['answer']).eq('is_run', True).execute()
  181. # return
  182. # else:
  183. # notify_line(data['id'], data['question'], file_path)
  184. # supabase.table('INNOLUX_cache').update({'is_run':None}).eq('question', data['question']).eq('answer', data['answer']).execute()
  185. # print(file_path)
  186. # return
  187. # except Exception as e:
  188. # print(f'Error: {e}')
  189. # supabase.table('INNOLUX_cache').update({'is_run':None}).eq('question', data['question']).eq('answer', data['answer']).execute()
  190. # else:
  191. # return
  192. # # 添加定时任务
  193. # scheduler.add_job(sub_1_minute, 'interval', minutes=0.8)
  194. # scheduler.start()
  195. # @app.on_event("shutdown")
  196. # def shutdown_event():
  197. # scheduler.shutdown()
  198. if __name__ == '__main__':
  199. uvicorn.run("台語tts:app", reload=False, port=8093, host='cmm.ai', ssl_keyfile="/etc/letsencrypt/live/cmm.ai/privkey.pem", ssl_certfile="/etc/letsencrypt/live/cmm.ai/fullchain.pem")