2021-03-01 19:16:33 +00:00
|
|
|
__filename__ = "speaker.py"
|
|
|
|
__author__ = "Bob Mottram"
|
|
|
|
__license__ = "AGPL3+"
|
|
|
|
__version__ = "1.2.0"
|
|
|
|
__maintainer__ = "Bob Mottram"
|
|
|
|
__email__ = "bob@freedombone.net"
|
|
|
|
__status__ = "Production"
|
|
|
|
|
2021-03-02 12:39:18 +00:00
|
|
|
import os
|
2021-03-01 19:16:33 +00:00
|
|
|
import random
|
|
|
|
from auth import createBasicAuthHeader
|
|
|
|
from session import getJson
|
|
|
|
from utils import getFullDomain
|
|
|
|
|
|
|
|
|
2021-03-02 15:13:10 +00:00
|
|
|
def getSpeakerPitch(displayName: str, screenreader: str) -> int:
|
2021-03-01 19:16:33 +00:00
|
|
|
"""Returns the speech synthesis pitch for the given name
|
|
|
|
"""
|
|
|
|
random.seed(displayName)
|
2021-03-02 15:13:10 +00:00
|
|
|
if screenreader == 'picospeaker':
|
2021-03-02 15:21:24 +00:00
|
|
|
return random.randint(-8, 3)
|
2021-03-01 19:16:33 +00:00
|
|
|
return random.randint(1, 100)
|
|
|
|
|
|
|
|
|
2021-03-02 15:13:10 +00:00
|
|
|
def getSpeakerRate(displayName: str, screenreader: str) -> int:
|
2021-03-01 19:16:33 +00:00
|
|
|
"""Returns the speech synthesis rate for the given name
|
|
|
|
"""
|
|
|
|
random.seed(displayName)
|
2021-03-02 15:13:10 +00:00
|
|
|
if screenreader == 'picospeaker':
|
2021-03-02 15:27:31 +00:00
|
|
|
return random.randint(0, 10)
|
2021-03-01 19:16:33 +00:00
|
|
|
return random.randint(50, 120)
|
|
|
|
|
|
|
|
|
|
|
|
def getSpeakerRange(displayName: str) -> int:
|
|
|
|
"""Returns the speech synthesis range for the given name
|
|
|
|
"""
|
|
|
|
random.seed(displayName)
|
|
|
|
return random.randint(300, 800)
|
|
|
|
|
|
|
|
|
2021-03-02 12:39:18 +00:00
|
|
|
def speakerPronounce(baseDir: str, sayText: str, translate: {}) -> str:
|
|
|
|
"""Screen readers may not always pronounce correctly, so you
|
|
|
|
can have a file which specifies conversions. File should contain
|
|
|
|
line items such as:
|
|
|
|
Epicyon -> Epi-cyon
|
|
|
|
"""
|
|
|
|
pronounceFilename = baseDir + '/accounts/speaker_pronounce.txt'
|
|
|
|
convertDict = {
|
|
|
|
"Epicyon": "Epi-cyon",
|
|
|
|
"espeak": "e-speak",
|
2021-03-02 12:50:07 +00:00
|
|
|
"clearnet": "clear-net",
|
2021-03-02 14:05:43 +00:00
|
|
|
"RT @": "Re-Tweet ",
|
2021-03-02 12:39:18 +00:00
|
|
|
"#": translate["hashtag"],
|
|
|
|
":)": translate["smile"],
|
|
|
|
";)": translate["wink"],
|
2021-03-02 17:11:49 +00:00
|
|
|
":(": translate["sad face"],
|
2021-03-02 12:39:18 +00:00
|
|
|
":-)": translate["smile"],
|
2021-03-02 17:11:49 +00:00
|
|
|
":-(": translate["sad face"],
|
2021-03-02 12:39:18 +00:00
|
|
|
";-)": translate["wink"],
|
|
|
|
"*": ""
|
|
|
|
}
|
|
|
|
if os.path.isfile(pronounceFilename):
|
|
|
|
with open(pronounceFilename, 'r') as fp:
|
|
|
|
pronounceList = fp.readlines()
|
|
|
|
for conversion in pronounceList:
|
|
|
|
separator = None
|
|
|
|
if '->' in conversion:
|
|
|
|
separator = '->'
|
|
|
|
elif ';' in conversion:
|
|
|
|
separator = ';'
|
|
|
|
elif ':' in conversion:
|
|
|
|
separator = ':'
|
|
|
|
elif ',' in conversion:
|
|
|
|
separator = ','
|
|
|
|
if not separator:
|
|
|
|
continue
|
|
|
|
|
|
|
|
text = conversion.split(separator)[0].strip()
|
|
|
|
converted = conversion.split(separator)[1].strip()
|
|
|
|
convertDict[text] = converted
|
|
|
|
for text, converted in convertDict.items():
|
|
|
|
if text in sayText:
|
|
|
|
sayText = sayText.replace(text, converted)
|
|
|
|
return sayText
|
|
|
|
|
|
|
|
|
2021-03-01 21:26:34 +00:00
|
|
|
def speakerReplaceLinks(sayText: str, translate: {},
|
|
|
|
detectedLinks: []) -> str:
|
2021-03-01 21:20:06 +00:00
|
|
|
"""Replaces any links in the given text with "link to [domain]".
|
|
|
|
Instead of reading out potentially very long and meaningless links
|
|
|
|
"""
|
2021-03-01 21:46:44 +00:00
|
|
|
removeChars = ('.\n', '. ', ',', ';', '?', '!')
|
2021-03-01 21:20:06 +00:00
|
|
|
text = sayText
|
|
|
|
for ch in removeChars:
|
|
|
|
text = text.replace(ch, ' ')
|
|
|
|
replacements = {}
|
|
|
|
wordsList = text.split(' ')
|
|
|
|
linkedStr = translate['Linked']
|
2021-03-02 14:05:43 +00:00
|
|
|
prevWord = ''
|
2021-03-01 21:20:06 +00:00
|
|
|
for word in wordsList:
|
2021-03-02 16:50:32 +00:00
|
|
|
if word.startswith(':'):
|
|
|
|
if word.endswith(':'):
|
|
|
|
replacements[word] = ', emoji ' + word.replace(':', '') + ','
|
|
|
|
continue
|
2021-03-02 14:05:43 +00:00
|
|
|
# replace mentions, but not re-tweets
|
2021-03-02 14:09:51 +00:00
|
|
|
if word.startswith('@') and not prevWord.endswith('RT'):
|
2021-03-02 13:54:22 +00:00
|
|
|
replacements[word] = \
|
2021-03-02 13:55:54 +00:00
|
|
|
translate['mentioning'] + ' ' + word[1:] + ','
|
2021-03-02 14:05:43 +00:00
|
|
|
prevWord = word
|
2021-03-02 13:54:22 +00:00
|
|
|
|
2021-03-01 21:20:06 +00:00
|
|
|
domain = None
|
|
|
|
domainFull = None
|
|
|
|
if 'https://' in word:
|
|
|
|
domain = word.split('https://')[1]
|
|
|
|
domainFull = 'https://' + domain
|
|
|
|
elif 'http://' in word:
|
|
|
|
domain = word.split('http://')[1]
|
|
|
|
domainFull = 'http://' + domain
|
|
|
|
if not domain:
|
|
|
|
continue
|
|
|
|
if '/' in domain:
|
|
|
|
domain = domain.split('/')[0]
|
2021-03-01 21:46:44 +00:00
|
|
|
if domain.startswith('www.'):
|
|
|
|
domain = domain.replace('www.', '')
|
2021-03-01 21:20:06 +00:00
|
|
|
replacements[domainFull] = '. ' + linkedStr + ' ' + domain + '.'
|
2021-03-01 21:26:34 +00:00
|
|
|
detectedLinks.append(domainFull)
|
2021-03-01 21:20:06 +00:00
|
|
|
for replaceStr, newStr in replacements.items():
|
|
|
|
sayText = sayText.replace(replaceStr, newStr)
|
2021-03-01 21:46:44 +00:00
|
|
|
return sayText.replace('..', '.')
|
2021-03-01 21:20:06 +00:00
|
|
|
|
|
|
|
|
2021-03-01 19:16:33 +00:00
|
|
|
def getSpeakerFromServer(baseDir: str, session,
|
|
|
|
nickname: str, password: str,
|
|
|
|
domain: str, port: int,
|
|
|
|
httpPrefix: str,
|
|
|
|
debug: bool, projectVersion: str) -> {}:
|
|
|
|
"""Returns some json which contains the latest inbox
|
|
|
|
entry in a minimal format suitable for a text-to-speech reader
|
|
|
|
"""
|
|
|
|
if not session:
|
|
|
|
print('WARN: No session for getSpeakerFromServer')
|
|
|
|
return 6
|
|
|
|
|
|
|
|
domainFull = getFullDomain(domain, port)
|
|
|
|
|
|
|
|
authHeader = createBasicAuthHeader(nickname, password)
|
|
|
|
|
|
|
|
headers = {
|
|
|
|
'host': domain,
|
|
|
|
'Content-type': 'application/json',
|
|
|
|
'Authorization': authHeader
|
|
|
|
}
|
|
|
|
|
|
|
|
url = \
|
|
|
|
httpPrefix + '://' + \
|
|
|
|
domainFull + '/users/' + nickname + '/speaker'
|
|
|
|
|
|
|
|
speakerJson = \
|
|
|
|
getJson(session, url, headers, None,
|
|
|
|
__version__, httpPrefix, domain)
|
|
|
|
return speakerJson
|