Skip to content

Commit

Permalink
add free edge tts engine
Browse files Browse the repository at this point in the history
  • Loading branch information
cdhigh committed May 4, 2024
1 parent 7299d31 commit 76bbbba
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -80,18 +80,19 @@ def convert(self, recipes, opts, file_ext, log, output_dir, fs):
self.aborted_articles = []
self.failed_downloads = []
for recipe in recipes:
indexFile = None
try:
ro = recipe(opts, log, output_dir, fs, feed_index_start=feed_index_start)
if recipeNum > 1: #只有单独推送才使用recipe的封面或报头
ro.cover_url = None
ro.masthead_url = None
ro.download()
indexFile = ro.download()
except Exception as e:
msg = 'Failed to execute recipe "{}": {}'.format(recipe.title, e)
log.warning(msg)
continue

if ro.feed_objects:
if indexFile and ro.feed_objects:
feed_index_start += len(ro.feed_objects)
self.feeds.extend(ro.feed_objects)
self.aborted_articles.extend(ro.aborted_articles)
Expand Down
3 changes: 2 additions & 1 deletion application/lib/ebook_tts/engines/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
from .tts_base import TTSBase
from .azure import AzureTTS
from .azure import AzureTTS, EdgeTTSFree
from .google import GoogleWebTTSFree, GoogleTextToSpeech
builtin_tts_engines = {
GoogleWebTTSFree.name: GoogleWebTTSFree,
GoogleTextToSpeech.name: GoogleTextToSpeech,
EdgeTTSFree.name: EdgeTTSFree,
AzureTTS.name: AzureTTS,
}
69 changes: 67 additions & 2 deletions application/lib/ebook_tts/engines/azure.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
from urllib.parse import urljoin
from urlopener import UrlOpener
from .tts_base import TTSBase
try:
import edge_tts
except:
edge_tts = None

#键为BCP-47语种代码,值为语音名字列表
azuretts_languages = {
Expand Down Expand Up @@ -203,7 +207,7 @@ class AzureTTS(TTSBase):
default_timeout = 60
#https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-services-quotas-and-limits
request_interval = 3 #20 transactions per 60 seconds
#每段音频不能超过10分钟,所以对于中文,大约2000字,因为大约1500 word
#每段音频不能超过10分钟,对于中文,大约2000字,因为大约1500 word
max_len_per_request = 1000
languages = azuretts_languages
regions = azure_regions
Expand Down Expand Up @@ -233,7 +237,7 @@ def voice_list(self):
else:
return {'status': UrlOpener.CodeMap(resp.status_code)}

#文本转换为语音,
#文本转换为语音,返回(mime, audio)
#支持的音频格式参考:
#<https://learn.microsoft.com/en-us/azure/ai-services/speech-service/rest-text-to-speech?tabs=streaming#audio-outputs>
def tts(self, text):
Expand All @@ -251,4 +255,65 @@ def tts(self, text):
raise Exception(self.opener.CodeMap(resp.status_code))


class EdgeTTSFree(TTSBase):
name = 'EdgeTTS(Free)'
alias = 'Microsoft Edge TTS (Free)'
need_api_key = False
api_key_hint = ''
default_api_host = ''
default_timeout = 60
#https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-services-quotas-and-limits
request_interval = 10 #20 transactions per 60 seconds
#每段音频不能超过10分钟,对于中文,大约2000字,因为大约1500 word
max_len_per_request = 1000
languages = azuretts_languages
regions = {}
engine_url = ''
region_url = ''
voice_url = ''
language_url = ''

def __init__(self, params):
import asyncio
super().__init__(params)
try:
self.eventLoop = asyncio.get_event_loop()
except Exception as e:
if str(e).startswith('There is no current event loop in thread'):
self.eventLoop = asyncio.new_event_loop()
asyncio.set_event_loop(self.eventLoop)
else:
default_log.warning(f'asyncio.get_event_loop failed: {e}')

#获取支持的语音列表
#或者可以直接到网页去查询
#https://learn.microsoft.com/en-us/azure/ai-services/speech-service/language-support?tabs=tts
def voice_list(self):
return edge_tts.list_voices()

#文本转换为语音,返回(mime, audio)
#支持的音频格式参考:
#<https://learn.microsoft.com/en-us/azure/ai-services/speech-service/rest-text-to-speech?tabs=streaming#audio-outputs>
def tts(self, text):
if not self.eventLoop:
raise Exception('Cannot create event_loop')

#将异步函数包装为同步调用
return self.eventLoop.run_until_complete(self.async_get_tts_audio(text))

#调用edge_tts的异步函数
async def async_get_tts_audio(self, text):
tts = edge_tts.Communicate(text,
voice=self.voice,
pitch=self.prosody_attributes['pitch'].get(self.pitch, '+0Hz'),
rate=self.prosody_attributes['rate'].get(self.rate, '+0%'),
volume=self.prosody_attributes['volume'].get(self.volume, '+0%'))
lines = []
try:
async for chunk in tts.stream():
if chunk["type"] == "audio":
lines.append(chunk["data"])
except Exception as e:
default_log.warning(f'EdgeTTSFree.async_get_tts_audio failed: {e}')
return '', b''
return 'audio/mpeg', b''.join(lines)
6 changes: 3 additions & 3 deletions application/lib/ebook_tts/engines/tts_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ class TTSBase:

#语音语调的允许常量值列表,除了使用常量值,也可以使用一个正负数值,比如 100%, +1.5, -30.00% 等
prosody_attributes = {
'rate': ('x-slow', 'slow', 'medium', 'fast', 'x-fast'),
'pitch': ('x-low', 'low', 'medium', 'high', 'x-high'),
'volume': ('silent', 'x-soft', 'soft', 'medium', 'loud', 'x-loud')
'rate': {'x-slow': '-50%', 'slow': '-25%', 'medium': '+0%', 'fast': '+25%', 'x-fast': '+50%'},
'pitch': {'x-low': '-50Hz', 'low': '-25Hz', 'medium': '+0Hz', 'high': '+25Hz', 'x-high': '+50Hz'},
'volume': {'silent': '-100%', 'x-soft': '-50%', 'soft': '-25%', 'medium': '+0%', 'loud': '+25%', 'x-loud': '+50%'}
}

def __init__(self, params):
Expand Down
3 changes: 2 additions & 1 deletion application/work/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ def mp3cat_path():
return execFile

#合并TTS生成的音频片段
#返回 (ext, audio)
def MergeAudioSegment(roList):
audioDirs = [ro.tts.get('audio_dir') for ro in roList if ro.tts.get('audio_dir')]
ret = ('', None)
Expand Down Expand Up @@ -259,6 +260,6 @@ def MergeAudioSegment(roList):
try:
shutil.rmtree(dir_)
except Exception as e:
default.log.debug(f"An error occurred while deleting '{item}': {e}")
default_log.debug(f"An error occurred while deleting '{dir_}': {e}")

return ret
3 changes: 2 additions & 1 deletion tools/update_req.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ def new_secret_key(length=12):
('six', '~=1.16.0'),
('feedparser', '~=6.0.11'),
('qrcode', '~=7.4.2'),
('#gtts', '~=2.5.1'),
('gtts', '~=2.5.1'),
('edge-tts', '~=6.1.11'),
]

REQ_DB = {
Expand Down

0 comments on commit 76bbbba

Please sign in to comment.