|
@@ -0,0 +1,238 @@
|
|
|
+import os
|
|
|
+# import requests
|
|
|
+# from bs4 import BeautifulSoup
|
|
|
+from selenium.webdriver.common.by import By
|
|
|
+# import re
|
|
|
+import time
|
|
|
+# from fake_useragent import UserAgent
|
|
|
+import undetected_chromedriver as uc
|
|
|
+from datetime import datetime
|
|
|
+import random
|
|
|
+import string
|
|
|
+# from selenium.webdriver.common.action_chains import ActionChains
|
|
|
+# from selenium.webdriver.common.keys import Keys
|
|
|
+# from selenium.webdriver.support.ui import WebDriverWait
|
|
|
+# from selenium.webdriver.support import expected_conditions as EC
|
|
|
+# from ga4mp import FirebaseMP
|
|
|
+from dotenv import load_dotenv
|
|
|
+import os
|
|
|
+import shutil
|
|
|
+import logging
|
|
|
+from fastapi import APIRouter, FastAPI
|
|
|
+import uvicorn
|
|
|
+from fastapi.middleware.cors import CORSMiddleware
|
|
|
+from supabase import create_client, Client
|
|
|
+
|
|
|
+load_dotenv()
|
|
|
+
|
|
|
+logging.basicConfig(level=logging.INFO)
|
|
|
+
|
|
|
+SUPABASE_URL: str = os.environ.get('SUPABASE_URL')
|
|
|
+SUPABASE_KEY: str = os.environ.get('SUPABASE_KEY')
|
|
|
+supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
|
|
|
+
|
|
|
+def download_blob(browser, blob_url, filename='output.mp3'):
|
|
|
+ # 使用 Selenium 獲取 Blob 內容
|
|
|
+ js_code = f"""
|
|
|
+ fetch('{blob_url}')
|
|
|
+ .then(response => response.blob())
|
|
|
+ .then(blob => {{
|
|
|
+ const url = URL.createObjectURL(blob);
|
|
|
+ const a = document.createElement('a');
|
|
|
+ a.href = url;
|
|
|
+ a.download = '{filename}';
|
|
|
+ document.body.appendChild(a);
|
|
|
+ a.click();
|
|
|
+ document.body.removeChild(a);
|
|
|
+ }})
|
|
|
+ .catch(error => console.error('Error downloading file:', error));
|
|
|
+ """
|
|
|
+ browser.execute_script(js_code)
|
|
|
+
|
|
|
+def tts_downloadfile(text):
|
|
|
+ start_time = time.time()
|
|
|
+ print(f'text長度: {len(text)}')
|
|
|
+ num = random.randint(3,5)
|
|
|
+ url = 'http://tts001.iptcloud.net:8804/'
|
|
|
+
|
|
|
+ default_download_folder = os.path.join(os.path.expanduser('~'), 'Downloads')
|
|
|
+ download_folder = '/var/www/html/innolux/tts_folder'
|
|
|
+ file_name = datetime.now().strftime(f"%Y%m%d%H%M%S_{''.join(random.sample(string.ascii_lowercase, 3))}.mp3")
|
|
|
+ # 替換為你希望的文件夾路徑
|
|
|
+ os.makedirs(download_folder, exist_ok=True)
|
|
|
+
|
|
|
+ options = uc.ChromeOptions()
|
|
|
+ options.add_argument('--ignore-certificate-errors')
|
|
|
+ prefs = {
|
|
|
+ "download.default_directory": download_folder, # 設定預設下載文件夾
|
|
|
+ "download.prompt_for_download": False,
|
|
|
+ "safebrowsing.enabled": True, # 確保安全瀏覽
|
|
|
+ }
|
|
|
+ options.add_experimental_option("prefs", prefs)
|
|
|
+ # options.add_argument('--incognito')
|
|
|
+ options.add_argument('--headless') # 如果不想顯示瀏覽器可以啟用這行
|
|
|
+ options.add_argument("--disable-gpu") # 禁用 GPU 加速
|
|
|
+
|
|
|
+ # 設置自定義 headers
|
|
|
+ with uc.Chrome(options=options, version_main=129) as browser:
|
|
|
+ try:
|
|
|
+ browser.get(url)
|
|
|
+
|
|
|
+ time.sleep(num)
|
|
|
+ # 轉中文成台語拼音
|
|
|
+ browser.find_element(By.XPATH, '//*[@id="js-input"]').send_keys(text)
|
|
|
+ time.sleep(0.1)
|
|
|
+ browser.find_element(By.XPATH, '//*[@id="js-translate"]').click()
|
|
|
+ time.sleep(0.1 + len(text)*0.01)
|
|
|
+
|
|
|
+ browser.execute_script('window.scrollBy(0, 200);')
|
|
|
+
|
|
|
+ # 轉語音
|
|
|
+ browser.find_element(By.XPATH, '//*[@id="button1"]').click()
|
|
|
+ # time.sleep(len(text)*0.6)
|
|
|
+
|
|
|
+ # audio_element = browser.find_element(By.XPATH, '//*[@id="audio1"]')
|
|
|
+ # time.sleep(0.2)
|
|
|
+ # # 取得 <audio> 標籤的屬性(例如 src)
|
|
|
+ audio_src = None
|
|
|
+ while not audio_src:
|
|
|
+ if time.time() - start_time > 45:
|
|
|
+ return 'Time exceeded'
|
|
|
+
|
|
|
+ audio_element = browser.find_element(By.XPATH, '//*[@id="audio1"]')
|
|
|
+ audio_src = audio_element.get_attribute('src')
|
|
|
+
|
|
|
+ if audio_src:
|
|
|
+ print("音頻來源:", audio_src)
|
|
|
+ download_blob(browser, audio_src, file_name)
|
|
|
+ else:
|
|
|
+ print("尚未檢測到音頻,繼續等待...")
|
|
|
+ time.sleep(0.3) # 每隔 0.3 秒檢測一次
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ # 下載音檔
|
|
|
+ # download_blob(browser, audio_src, file_name)
|
|
|
+ is_default = False
|
|
|
+ # check 是否下載完成
|
|
|
+ file_path = '/var/www/html/innolux/tts_folder' + '/' + file_name
|
|
|
+ default_file_path = '/root/Downloads' + '/' + file_name
|
|
|
+ while not os.path.exists(file_path):
|
|
|
+ print('...')
|
|
|
+ # if default_file_path:
|
|
|
+ # is_default = True
|
|
|
+ # break
|
|
|
+ time.sleep(0.001)
|
|
|
+ # 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"))
|
|
|
+ # shutil.move(file_path, destination_path)
|
|
|
+ # if is_default:
|
|
|
+ # while not os.path.exists(default_file_path):
|
|
|
+ # print('...')
|
|
|
+ # time.sleep(0.001)
|
|
|
+ # shutil.move(default_file_path, file_path)
|
|
|
+ # print("檔案移動完成")
|
|
|
+ print(f"下載完成: {file_path}")
|
|
|
+ file_path = file_path.split('html/')[1]
|
|
|
+ print(file_path)
|
|
|
+ print(time.time() - start_time)
|
|
|
+
|
|
|
+ return file_path
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ print(f'Error: {e}')
|
|
|
+ return e
|
|
|
+
|
|
|
+
|
|
|
+app = FastAPI()
|
|
|
+
|
|
|
+app.add_middleware(
|
|
|
+ CORSMiddleware,
|
|
|
+ allow_origins=["*"],
|
|
|
+ allow_credentials=True,
|
|
|
+ allow_methods=["*"],
|
|
|
+ allow_headers=["*"],
|
|
|
+)
|
|
|
+
|
|
|
+
|
|
|
+@app.post('/tts')
|
|
|
+async def tts(answer: str):
|
|
|
+ file_path = tts_downloadfile(answer)
|
|
|
+ # if '.mp3' not in file_path:
|
|
|
+ # return {"message": file_path}
|
|
|
+ if file_path:
|
|
|
+ return {'message': {'mp3_url': file_path}}
|
|
|
+ else:
|
|
|
+ return {"message": "tts processing failed."}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+# from apscheduler.schedulers.background import BackgroundScheduler
|
|
|
+# import asyncio
|
|
|
+# import requests
|
|
|
+
|
|
|
+# scheduler = BackgroundScheduler()
|
|
|
+# loop = asyncio.get_event_loop()
|
|
|
+
|
|
|
+# # 巴巴群組
|
|
|
+# def notify_line(id, question, message):
|
|
|
+# # url = 'http://cmm.ai:3001/api/push/PLSDC1vOG9'
|
|
|
+# url = 'https://notify-api.line.me/api/notify'
|
|
|
+# token = 'OtAC4mBxi1tHjFT5RDcCiA8NwNKuxHVOAlZU5iO04XB' # 巴巴工程師群組
|
|
|
+# headers = {
|
|
|
+# 'Authorization': 'Bearer ' + token
|
|
|
+# }
|
|
|
+# # 構造請求的數據
|
|
|
+# data = {
|
|
|
+# 'message': f"\n群創\nid:【{id}】\n問題: {question}\nmessage: {message}"
|
|
|
+# }
|
|
|
+
|
|
|
+# try:
|
|
|
+# response = requests.post(url, headers=headers, data=data)
|
|
|
+# if response.status_code == 200:
|
|
|
+# print("Notification sent successfully.")
|
|
|
+# else:
|
|
|
+# print(f"Failed to send notification. Status code: {response.status_code}")
|
|
|
+# except Exception as e:
|
|
|
+# print(f"An error occurred: {e}")
|
|
|
+
|
|
|
+# def sub_1_minute():
|
|
|
+# time.sleep(1)
|
|
|
+# cursor = supabase.table('INNOLUX_cache').select('*').filter('mp3_url', 'is', 'null').order('id', desc=False).execute()
|
|
|
+# if cursor.data:
|
|
|
+# data = cursor.data[0]
|
|
|
+# if data['is_run']:
|
|
|
+# return
|
|
|
+# try:
|
|
|
+# supabase.table('INNOLUX_cache').update({'is_run':True}).eq('question', data['question']).eq('answer', data['answer']).execute()
|
|
|
+# file_path = tts_downloadfile(data['answer'])
|
|
|
+# if '.mp3' in file_path:
|
|
|
+# supabase.table('INNOLUX_cache').update({'mp3_url':file_path}).eq('question', data['question']).eq('answer', data['answer']).eq('is_run', True).execute()
|
|
|
+# print(f'{file_path} 已存入資料庫')
|
|
|
+# supabase.table('INNOLUX_cache').update({'is_run':None}).eq('question', data['question']).eq('answer', data['answer']).eq('is_run', True).execute()
|
|
|
+# return
|
|
|
+# else:
|
|
|
+# notify_line(data['id'], data['question'], file_path)
|
|
|
+# supabase.table('INNOLUX_cache').update({'is_run':None}).eq('question', data['question']).eq('answer', data['answer']).execute()
|
|
|
+# print(file_path)
|
|
|
+# return
|
|
|
+# except Exception as e:
|
|
|
+# print(f'Error: {e}')
|
|
|
+# supabase.table('INNOLUX_cache').update({'is_run':None}).eq('question', data['question']).eq('answer', data['answer']).execute()
|
|
|
+# else:
|
|
|
+# return
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+# # 添加定时任务
|
|
|
+# scheduler.add_job(sub_1_minute, 'interval', minutes=0.8)
|
|
|
+# scheduler.start()
|
|
|
+
|
|
|
+# @app.on_event("shutdown")
|
|
|
+# def shutdown_event():
|
|
|
+# scheduler.shutdown()
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == '__main__':
|
|
|
+ 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")
|