From 6a33bce7c1fe3eb4285f331f702f884184f51e84 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 1 Mar 2021 19:16:33 +0000 Subject: [PATCH] Speaker option --- epicyon.py | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ speaker.py | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 speaker.py diff --git a/epicyon.py b/epicyon.py index ea958dd75..4c6d2a354 100644 --- a/epicyon.py +++ b/epicyon.py @@ -75,6 +75,10 @@ from theme import setTheme from announce import sendAnnounceViaServer from socnet import instancesGraph from migrate import migrateAccounts +from speaker import getSpeakerFromServer +from speaker import getSpeakerPitch +from speaker import getSpeakerRate +from speaker import getSpeakerRange import argparse @@ -429,6 +433,10 @@ parser.add_argument('--level', dest='skillLevelPercent', type=int, parser.add_argument('--status', '--availability', dest='availability', type=str, default=None, help='Set an availability status') +parser.add_argument('--speaker', '--tts', dest='speaker', + type=str, default=None, + help='Announce posts as they arrive at your ' + + 'inbox using TTS. --speaker [handle]') parser.add_argument('--block', dest='block', type=str, default=None, help='Block a particular address') parser.add_argument('--unblock', dest='unblock', type=str, default=None, @@ -1887,6 +1895,65 @@ if args.availability: time.sleep(1) sys.exit() +if args.speaker: + # Announce posts as they arrive in your inbox using text-to-speech + if args.speaker.startswith('@'): + args.speaker = args.speaker[1:] + if '@' not in args.speaker: + print('Specify the handle of the speaker nickname@domain') + sys.exit() + nickname = args.speaker.split('@')[0] + domain = args.speaker.split('@')[1] + + if not nickname: + print('Specify a nickname with the --nickname option') + sys.exit() + + if not args.password: + print('Specify a password with the --password option') + sys.exit() + + proxyType = None + if args.tor or domain.endswith('.onion'): + proxyType = 'tor' + if domain.endswith('.onion'): + args.port = 80 + elif args.i2p or domain.endswith('.i2p'): + proxyType = 'i2p' + if domain.endswith('.i2p'): + args.port = 80 + elif args.gnunet: + proxyType = 'gnunet' + + print('Setting up espeak') + from espeak import espeak + + session = createSession(proxyType) + print('Running speaker for ' + nickname + '@' + domain) + + prevSay = '' + while (1): + speakerJson = \ + getSpeakerFromServer(baseDir, session, nickname, args.password, + domain, port, + httpPrefix, + True, __version__) + if speakerJson: + if speakerJson['say'] != prevSay: + print(speakerJson['name'] + ': ' + speakerJson['say'] + '\n') + pitch = getSpeakerPitch(speakerJson['name']) + espeak.set_parameter(espeak.Parameter.Pitch, pitch) + rate = getSpeakerRate(speakerJson['name']) + espeak.set_parameter(espeak.Parameter.Rate, 110) + srange = getSpeakerRange(speakerJson['name']) + espeak.set_parameter(espeak.Parameter.Range, srange) + espeak.synth(speakerJson['name']) + time.sleep(3) + espeak.synth(speakerJson['say']) + prevSay = speakerJson['say'] + time.sleep(20) + sys.exit() + if federationList: print('Federating with: ' + str(federationList)) diff --git a/speaker.py b/speaker.py new file mode 100644 index 000000000..ea3df1141 --- /dev/null +++ b/speaker.py @@ -0,0 +1,65 @@ +__filename__ = "speaker.py" +__author__ = "Bob Mottram" +__license__ = "AGPL3+" +__version__ = "1.2.0" +__maintainer__ = "Bob Mottram" +__email__ = "bob@freedombone.net" +__status__ = "Production" + +import random +from auth import createBasicAuthHeader +from session import getJson +from utils import getFullDomain + + +def getSpeakerPitch(displayName: str) -> int: + """Returns the speech synthesis pitch for the given name + """ + random.seed(displayName) + return random.randint(1, 100) + + +def getSpeakerRate(displayName: str) -> int: + """Returns the speech synthesis rate for the given name + """ + random.seed(displayName) + 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) + + +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