From c9e36e857741d79720bf02b8ecba8c3a2bba07af Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Thu, 18 Mar 2021 17:27:46 +0000 Subject: [PATCH] Add speakable text for desktop client --- daemon.py | 31 +++++-------------------------- desktop_client.py | 20 +++++++++++++++----- epicyon.py | 1 + speaker.py | 24 ++++++++++++++++++++++++ utils.py | 29 +++++++++++++++++++++++++++++ 5 files changed, 74 insertions(+), 31 deletions(-) diff --git a/daemon.py b/daemon.py index c7edcc342..219f2ed28 100644 --- a/daemon.py +++ b/daemon.py @@ -10,7 +10,6 @@ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer, HTTPServer import sys import json import time -import locale import urllib.parse import datetime from socket import error as SocketError @@ -192,6 +191,7 @@ from shares import addShare from shares import removeShare from shares import expireShares from categories import setHashtagCategory +from utils import loadTranslationsFromFile from utils import getLocalNetworkAddresses from utils import decodedHost from utils import isPublicPost @@ -14443,32 +14443,11 @@ def runDaemon(brochMode: bool, httpd.translate = {} httpd.systemLanguage = 'en' if not unitTest: - if not os.path.isdir(baseDir + '/translations'): - print('ERROR: translations directory not found') - return - if not language: - systemLanguage = locale.getdefaultlocale()[0] - else: - systemLanguage = language - if not systemLanguage: - systemLanguage = 'en' - if '_' in systemLanguage: - systemLanguage = systemLanguage.split('_')[0] - while '/' in systemLanguage: - systemLanguage = systemLanguage.split('/')[1] - if '.' in systemLanguage: - systemLanguage = systemLanguage.split('.')[0] - translationsFile = baseDir + '/translations/' + \ - systemLanguage + '.json' - if not os.path.isfile(translationsFile): - systemLanguage = 'en' - translationsFile = baseDir + '/translations/' + \ - systemLanguage + '.json' - print('System language: ' + systemLanguage) - httpd.systemLanguage = systemLanguage - httpd.translate = loadJson(translationsFile) + httpd.translate, httpd.systemLanguage = \ + loadTranslationsFromFile(baseDir, language) + print('System language: ' + httpd.systemLanguage) if not httpd.translate: - print('ERROR: no translations loaded from ' + translationsFile) + print('ERROR: no translations were loaded') sys.exit() # For moderated newswire feeds this is the amount of time allowed diff --git a/desktop_client.py b/desktop_client.py index d8a8d128c..c1c3eac7e 100644 --- a/desktop_client.py +++ b/desktop_client.py @@ -15,6 +15,7 @@ import webbrowser import urllib.parse from pathlib import Path from random import randint +from utils import loadTranslationsFromFile from utils import removeHtml from utils import getStatusNumber from utils import loadJson @@ -24,6 +25,7 @@ from utils import getDomainFromActor from utils import getFullDomain from utils import isPGPEncrypted from session import createSession +from speaker import speakableText from speaker import getSpeakerPitch from speaker import getSpeakerRate from speaker import getSpeakerRange @@ -425,10 +427,11 @@ def _textOnlyContent(content: str) -> str: return removeHtml(content) -def _readLocalBoxPost(boxName: str, +def _readLocalBoxPost(baseDir: str, boxName: str, pageNumber: int, index: int, boxJson: {}, systemLanguage: str, - screenreader: str, espeak) -> {}: + screenreader: str, espeak, + translate: {}) -> {}: """Reads a post from the given timeline Returns the speaker json """ @@ -458,7 +461,7 @@ def _readLocalBoxPost(boxName: str, return content = _safeMessage(content) - messageStr = content + messageStr = speakableText(baseDir, content, translate) if screenreader: time.sleep(2) @@ -911,6 +914,7 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str, noKeyPress: bool, storeInboxPosts: bool, showNewPosts: bool, + language: str, debug: bool) -> None: """Runs the desktop and screen reader client, which announces new inbox items @@ -977,6 +981,12 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str, newDMsExist = False pgpKeyUpload = False + sayStr = indent + 'Loading translations file...' + _sayCommand(sayStr, sayStr, screenreader, + systemLanguage, espeak) + translate, systemLanguage = \ + loadTranslationsFromFile(baseDir, language) + sayStr = indent + 'Connecting...' _sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak) @@ -1124,10 +1134,10 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str, if boxJson and postIndexStr.isdigit(): postIndex = int(postIndexStr) postJsonObject = \ - _readLocalBoxPost(currTimeline, + _readLocalBoxPost(baseDir, currTimeline, pageNumber, postIndex, boxJson, systemLanguage, screenreader, - espeak) + espeak, translate) print('') elif commandStr == 'reply' or commandStr == 'r': if postJsonObject: diff --git a/epicyon.py b/epicyon.py index 3052c1f06..4e3971f24 100644 --- a/epicyon.py +++ b/epicyon.py @@ -1926,6 +1926,7 @@ if args.desktop: args.noKeyPress, storeInboxPosts, args.notifyShowNewPosts, + args.language, args.debug) sys.exit() diff --git a/speaker.py b/speaker.py index 2ec228b95..f2561fd87 100644 --- a/speaker.py +++ b/speaker.py @@ -405,6 +405,30 @@ def getSSMLbox(baseDir: str, path: str, instanceTitle, gender) +def speakableText(baseDir: str, content: str, translate: {}) -> str: + """Convert the given text to a speakable version + which includes changes for prononciation + """ + if isPGPEncrypted(content): + return + + # replace some emoji before removing html + if ' <3' in content: + content = content.replace(' <3', ' ' + translate['heart']) + content = removeHtml(htmlReplaceQuoteMarks(content)) + detectedLinks = [] + content = speakerReplaceLinks(content, translate, detectedLinks) + # replace all double spaces + while ' ' in content: + content = content.replace(' ', ' ') + content = content.replace(' . ', '. ').strip() + sayContent = _speakerPronounce(baseDir, content, translate) + # replace all double spaces + while ' ' in sayContent: + sayContent = sayContent.replace(' ', ' ') + return sayContent.replace(' . ', '. ').strip() + + def _postToSpeakerJson(baseDir: str, httpPrefix: str, nickname: str, domain: str, domainFull: str, postJsonObject: {}, personCache: {}, diff --git a/utils.py b/utils.py index 35d42a4e8..35dc00072 100644 --- a/utils.py +++ b/utils.py @@ -13,6 +13,7 @@ import shutil import datetime import json import idna +import locale from pprint import pprint from calendar import monthrange from followingCalendar import addPersonToCalendar @@ -2150,3 +2151,31 @@ def isPGPEncrypted(content: str) -> bool: if '--END PGP MESSAGE--' in content: return True return False + + +def loadTranslationsFromFile(baseDir: str, language: str) -> ({}, str): + """Returns the translations dictionary + """ + if not os.path.isdir(baseDir + '/translations'): + print('ERROR: translations directory not found') + return + if not language: + systemLanguage = locale.getdefaultlocale()[0] + else: + systemLanguage = language + if not systemLanguage: + systemLanguage = 'en' + if '_' in systemLanguage: + systemLanguage = systemLanguage.split('_')[0] + while '/' in systemLanguage: + systemLanguage = systemLanguage.split('/')[1] + if '.' in systemLanguage: + systemLanguage = systemLanguage.split('.')[0] + translationsFile = baseDir + '/translations/' + \ + systemLanguage + '.json' + if not os.path.isfile(translationsFile): + systemLanguage = 'en' + translationsFile = baseDir + '/translations/' + \ + systemLanguage + '.json' + print('System language: ' + systemLanguage) + return loadJson(translationsFile), systemLanguage