Merge branch 'main' of gitlab.com:bashrc2/epicyon
85
daemon.py
|
@ -617,6 +617,17 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
return True
|
||||
return False
|
||||
|
||||
def _request_ssml(self) -> bool:
|
||||
"""Should a ssml response be given?
|
||||
"""
|
||||
if not self.headers.get('Accept'):
|
||||
return False
|
||||
accept_str = self.headers['Accept']
|
||||
if 'application/ssml' in accept_str:
|
||||
if 'text/html' not in accept_str:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _request_http(self) -> bool:
|
||||
"""Should a http response be given?
|
||||
"""
|
||||
|
@ -11025,7 +11036,7 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
self._redirect_headers(actor_absolute, cookie, calling_domain)
|
||||
return True
|
||||
|
||||
def _show_individual_at_post(self, authorized: bool,
|
||||
def _show_individual_at_post(self, ssml_getreq: bool, authorized: bool,
|
||||
calling_domain: str, referer_domain: str,
|
||||
path: str,
|
||||
base_dir: str, http_prefix: str,
|
||||
|
@ -11073,6 +11084,35 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
if len(status_number) <= 10 or not status_number.isdigit():
|
||||
return False
|
||||
|
||||
if ssml_getreq:
|
||||
ssml_filename = \
|
||||
acct_dir(base_dir, nickname, domain) + '/outbox/' + \
|
||||
http_prefix + ':##' + domain_full + '#users#' + nickname + \
|
||||
'#statuses#' + status_number + '.ssml'
|
||||
if not os.path.isfile(ssml_filename):
|
||||
ssml_filename = \
|
||||
acct_dir(base_dir, nickname, domain) + '/postcache/' + \
|
||||
http_prefix + ':##' + domain_full + '#users#' + \
|
||||
nickname + '#statuses#' + status_number + '.ssml'
|
||||
if not os.path.isfile(ssml_filename):
|
||||
self._404()
|
||||
return True
|
||||
ssml_str = None
|
||||
try:
|
||||
with open(ssml_filename, 'r') as fp_ssml:
|
||||
ssml_str = fp_ssml.read()
|
||||
except OSError:
|
||||
pass
|
||||
if ssml_str:
|
||||
msg = ssml_str.encode('utf-8')
|
||||
msglen = len(msg)
|
||||
self._set_headers('application/ssml+xml', msglen,
|
||||
cookie, calling_domain, False)
|
||||
self._write(msg)
|
||||
return True
|
||||
self._404()
|
||||
return True
|
||||
|
||||
post_filename = \
|
||||
acct_dir(base_dir, nickname, domain) + '/outbox/' + \
|
||||
http_prefix + ':##' + domain_full + '#users#' + nickname + \
|
||||
|
@ -11346,7 +11386,7 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
self.server.getreq_busy = False
|
||||
return True
|
||||
|
||||
def _show_individual_post(self, authorized: bool,
|
||||
def _show_individual_post(self, ssml_getreq: bool, authorized: bool,
|
||||
calling_domain: str, referer_domain: str,
|
||||
path: str,
|
||||
base_dir: str, http_prefix: str,
|
||||
|
@ -11388,6 +11428,35 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
if len(status_number) <= 10 or (not status_number.isdigit()):
|
||||
return False
|
||||
|
||||
if ssml_getreq:
|
||||
ssml_filename = \
|
||||
acct_dir(base_dir, nickname, domain) + '/outbox/' + \
|
||||
http_prefix + ':##' + domain_full + '#users#' + nickname + \
|
||||
'#statuses#' + status_number + '.ssml'
|
||||
if not os.path.isfile(ssml_filename):
|
||||
ssml_filename = \
|
||||
acct_dir(base_dir, nickname, domain) + '/postcache/' + \
|
||||
http_prefix + ':##' + domain_full + '#users#' + \
|
||||
nickname + '#statuses#' + status_number + '.ssml'
|
||||
if not os.path.isfile(ssml_filename):
|
||||
self._404()
|
||||
return True
|
||||
ssml_str = None
|
||||
try:
|
||||
with open(ssml_filename, 'r') as fp_ssml:
|
||||
ssml_str = fp_ssml.read()
|
||||
except OSError:
|
||||
pass
|
||||
if ssml_str:
|
||||
msg = ssml_str.encode('utf-8')
|
||||
msglen = len(msg)
|
||||
self._set_headers('application/ssml+xml', msglen,
|
||||
cookie, calling_domain, False)
|
||||
self._write(msg)
|
||||
return True
|
||||
self._404()
|
||||
return True
|
||||
|
||||
post_filename = \
|
||||
acct_dir(base_dir, nickname, domain) + '/outbox/' + \
|
||||
http_prefix + ':##' + domain_full + '#users#' + nickname + \
|
||||
|
@ -15387,12 +15456,15 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
'_GET', 'create session',
|
||||
self.server.debug)
|
||||
|
||||
# is this a html request?
|
||||
# is this a html/ssml/icalendar request?
|
||||
html_getreq = False
|
||||
ssml_getreq = False
|
||||
icalendar_getreq = False
|
||||
if self._has_accept(calling_domain):
|
||||
if self._request_http():
|
||||
html_getreq = True
|
||||
elif self._request_ssml():
|
||||
ssml_getreq = True
|
||||
elif self._request_icalendar():
|
||||
icalendar_getreq = True
|
||||
else:
|
||||
|
@ -16184,7 +16256,8 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
self.server.translate,
|
||||
access_keys,
|
||||
self.server.access_keys,
|
||||
self.server.default_timeline)
|
||||
self.server.default_timeline,
|
||||
self.server.theme_name)
|
||||
msg = msg.encode('utf-8')
|
||||
msglen = len(msg)
|
||||
self._login_headers('text/html', msglen, calling_domain)
|
||||
|
@ -17628,7 +17701,7 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
self.server.debug)
|
||||
|
||||
# get an individual post from the path /@nickname/statusnumber
|
||||
if self._show_individual_at_post(authorized,
|
||||
if self._show_individual_at_post(ssml_getreq, authorized,
|
||||
calling_domain, referer_domain,
|
||||
self.path,
|
||||
self.server.base_dir,
|
||||
|
@ -17773,7 +17846,7 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
# get an individual post from the path
|
||||
# /users/nickname/statuses/number
|
||||
if '/statuses/' in self.path and users_in_path:
|
||||
if self._show_individual_post(authorized,
|
||||
if self._show_individual_post(ssml_getreq, authorized,
|
||||
calling_domain, referer_domain,
|
||||
self.path,
|
||||
self.server.base_dir,
|
||||
|
|
38
epicyon.py
|
@ -46,6 +46,7 @@ from session import create_session
|
|||
from session import get_json
|
||||
from session import get_vcard
|
||||
from session import download_html
|
||||
from session import download_ssml
|
||||
from newswire import get_rss
|
||||
from filters import add_filter
|
||||
from filters import remove_filter
|
||||
|
@ -341,6 +342,8 @@ parser.add_argument('--xmlvcard', dest='xmlvcard', type=str, default=None,
|
|||
'activitypub actor url')
|
||||
parser.add_argument('--json', dest='json', type=str, default=None,
|
||||
help='Show the json for a given activitypub url')
|
||||
parser.add_argument('--ssml', dest='ssml', type=str, default=None,
|
||||
help='Show the SSML for a given activitypub url')
|
||||
parser.add_argument('--htmlpost', dest='htmlpost', type=str, default=None,
|
||||
help='Show the html for a given activitypub url')
|
||||
parser.add_argument('--rss', dest='rss', type=str, default=None,
|
||||
|
@ -1040,6 +1043,31 @@ if args.json:
|
|||
pprint(test_json)
|
||||
sys.exit()
|
||||
|
||||
if args.ssml:
|
||||
session = create_session(None)
|
||||
profile_str = 'https://www.w3.org/ns/activitystreams'
|
||||
as_header = {
|
||||
'Accept': 'application/ssml+xml; profile="' + profile_str + '"'
|
||||
}
|
||||
if not args.domain:
|
||||
args.domain = get_config_param(base_dir, 'domain')
|
||||
domain = ''
|
||||
if args.domain:
|
||||
domain = args.domain
|
||||
signing_priv_key_pem = get_instance_actor_key(base_dir, domain)
|
||||
if debug:
|
||||
print('base_dir: ' + str(base_dir))
|
||||
if signing_priv_key_pem:
|
||||
print('Obtained instance actor signing key')
|
||||
else:
|
||||
print('Did not obtain instance actor key for ' + domain)
|
||||
test_ssml = download_ssml(signing_priv_key_pem, session, args.ssml,
|
||||
as_header, None, debug, __version__,
|
||||
http_prefix, domain)
|
||||
if test_ssml:
|
||||
print(str(test_ssml))
|
||||
sys.exit()
|
||||
|
||||
if args.vcard:
|
||||
session = create_session(None)
|
||||
if not args.domain:
|
||||
|
@ -1084,11 +1112,11 @@ if args.htmlpost:
|
|||
print('Obtained instance actor signing key')
|
||||
else:
|
||||
print('Did not obtain instance actor key for ' + domain)
|
||||
testHtml = download_html(signing_priv_key_pem, session, args.htmlpost,
|
||||
as_header, None, debug, __version__,
|
||||
http_prefix, domain)
|
||||
if testHtml:
|
||||
print(testHtml)
|
||||
test_html = download_html(signing_priv_key_pem, session, args.htmlpost,
|
||||
as_header, None, debug, __version__,
|
||||
http_prefix, domain)
|
||||
if test_html:
|
||||
print(test_html)
|
||||
sys.exit()
|
||||
|
||||
# create cache for actors
|
||||
|
|
6
inbox.py
|
@ -2282,7 +2282,8 @@ def _receive_announce(recent_posts_cache: {},
|
|||
nickname, domain, domain_full,
|
||||
post_json_object, person_cache,
|
||||
translate, lookup_actor,
|
||||
theme_name)
|
||||
theme_name, system_language,
|
||||
'inbox')
|
||||
try:
|
||||
with open(post_filename + '.tts', 'w+') as ttsfile:
|
||||
ttsfile.write('\n')
|
||||
|
@ -4120,7 +4121,8 @@ def _inbox_after_initial(server, inbox_start_time,
|
|||
update_speaker(base_dir, http_prefix,
|
||||
nickname, domain, domain_full,
|
||||
post_json_object, person_cache,
|
||||
translate, None, theme_name)
|
||||
translate, None, theme_name,
|
||||
system_language, boxname)
|
||||
fitness_performance(inbox_start_time,
|
||||
server.fitness,
|
||||
'INBOX', 'update_speaker',
|
||||
|
|
|
@ -57,6 +57,7 @@ from delete import outbox_delete
|
|||
from shares import outbox_share_upload
|
||||
from shares import outbox_undo_share_upload
|
||||
from webapp_post import individual_post_as_html
|
||||
from speaker import update_speaker
|
||||
|
||||
|
||||
def _person_receive_update_outbox(recent_posts_cache: {},
|
||||
|
@ -400,6 +401,13 @@ def post_message_to_outbox(session, translate: {},
|
|||
print('WARN: post not saved to outbox ' + outbox_name)
|
||||
return False
|
||||
|
||||
update_speaker(base_dir, http_prefix,
|
||||
post_to_nickname, domain, domain_full,
|
||||
message_json, person_cache,
|
||||
translate, message_json['actor'],
|
||||
theme, system_language,
|
||||
outbox_name)
|
||||
|
||||
# save all instance blogs to the news actor
|
||||
if post_to_nickname != 'news' and outbox_name == 'tlblogs':
|
||||
if '/' in saved_filename:
|
||||
|
|
39
session.py
|
@ -373,6 +373,45 @@ def download_html(signing_priv_key_pem: str,
|
|||
None, quiet, debug, False)
|
||||
|
||||
|
||||
def download_ssml(signing_priv_key_pem: str,
|
||||
session, url: str, headers: {}, params: {}, debug: bool,
|
||||
version: str = __version__, http_prefix: str = 'https',
|
||||
domain: str = 'testdomain',
|
||||
timeout_sec: int = 20, quiet: bool = False) -> {}:
|
||||
if not isinstance(url, str):
|
||||
if debug and not quiet:
|
||||
print('url: ' + str(url))
|
||||
print('ERROR: download_ssml failed, url should be a string')
|
||||
return None
|
||||
session_params = {}
|
||||
session_headers = {}
|
||||
if headers:
|
||||
session_headers = headers
|
||||
if params:
|
||||
session_params = params
|
||||
session_headers['Accept'] = 'application/ssml+xml'
|
||||
session_headers['User-Agent'] = 'Epicyon/' + version
|
||||
if domain:
|
||||
session_headers['User-Agent'] += \
|
||||
'; +' + http_prefix + '://' + domain + '/'
|
||||
if not session:
|
||||
if not quiet:
|
||||
print('WARN: download_ssml failed, no session specified')
|
||||
return None
|
||||
|
||||
if debug:
|
||||
HTTPConnection.debuglevel = 1
|
||||
|
||||
if signing_priv_key_pem:
|
||||
return _get_json_signed(session, url, domain,
|
||||
session_headers, session_params,
|
||||
timeout_sec, signing_priv_key_pem,
|
||||
quiet, debug)
|
||||
return _get_json_request(session, url, domain, session_headers,
|
||||
session_params, timeout_sec,
|
||||
None, quiet, debug, False)
|
||||
|
||||
|
||||
def _set_user_agent(session, http_prefix: str, domain_full: str) -> None:
|
||||
"""Sets the user agent
|
||||
"""
|
||||
|
|
52
speaker.py
|
@ -11,6 +11,7 @@ import os
|
|||
import html
|
||||
import random
|
||||
import urllib.parse
|
||||
from utils import get_cached_post_filename
|
||||
from utils import remove_id_ending
|
||||
from utils import is_dm
|
||||
from utils import is_reply
|
||||
|
@ -301,9 +302,11 @@ def _speaker_endpoint_json(display_name: str, summary: str,
|
|||
return speaker_json
|
||||
|
||||
|
||||
def _ssm_lheader(system_language: str, instance_title: str) -> str:
|
||||
def _ssml_header(system_language: str, box_name: str, summary: str) -> str:
|
||||
"""Returns a header for an SSML document
|
||||
"""
|
||||
if summary:
|
||||
summary = ': ' + summary
|
||||
return '<?xml version="1.0"?>\n' + \
|
||||
'<speak xmlns="http://www.w3.org/2001/10/synthesis"\n' + \
|
||||
' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n' + \
|
||||
|
@ -312,15 +315,14 @@ def _ssm_lheader(system_language: str, instance_title: str) -> str:
|
|||
' version="1.1">\n' + \
|
||||
' <metadata>\n' + \
|
||||
' <dc:title xml:lang="' + system_language + '">' + \
|
||||
instance_title + ' inbox</dc:title>\n' + \
|
||||
box_name + summary + '</dc:title>\n' + \
|
||||
' </metadata>\n'
|
||||
|
||||
|
||||
def _speaker_endpoint_ssml(display_name: str, summary: str,
|
||||
content: str, image_description: str,
|
||||
links: [], language: str,
|
||||
instance_title: str,
|
||||
gender: str) -> str:
|
||||
gender: str, box_name: str) -> str:
|
||||
"""Returns an SSML endpoint for the TTS speaker
|
||||
https://en.wikipedia.org/wiki/Speech_Synthesis_Markup_Language
|
||||
https://www.w3.org/TR/speech-synthesis/
|
||||
|
@ -342,7 +344,9 @@ def _speaker_endpoint_ssml(display_name: str, summary: str,
|
|||
|
||||
content = _add_ssm_lemphasis(content)
|
||||
voice_params = 'name="' + display_name + '" gender="' + gender + '"'
|
||||
return _ssm_lheader(lang_short, instance_title) + \
|
||||
if summary is None:
|
||||
summary = ''
|
||||
return _ssml_header(lang_short, box_name, summary) + \
|
||||
' <p>\n' + \
|
||||
' <s xml:lang="' + language + '">\n' + \
|
||||
' <voice ' + voice_params + '>\n' + \
|
||||
|
@ -356,7 +360,6 @@ def _speaker_endpoint_ssml(display_name: str, summary: str,
|
|||
def get_ssml_box(base_dir: str, path: str,
|
||||
domain: str,
|
||||
system_language: str,
|
||||
instance_title: str,
|
||||
box_name: str) -> str:
|
||||
"""Returns SSML for the given timeline
|
||||
"""
|
||||
|
@ -379,7 +382,7 @@ def get_ssml_box(base_dir: str, path: str,
|
|||
speaker_json['imageDescription'],
|
||||
speaker_json['detectedLinks'],
|
||||
system_language,
|
||||
instance_title, gender)
|
||||
gender, box_name)
|
||||
|
||||
|
||||
def speakable_text(base_dir: str, content: str, translate: {}) -> (str, []):
|
||||
|
@ -544,7 +547,8 @@ def update_speaker(base_dir: str, http_prefix: str,
|
|||
nickname: str, domain: str, domain_full: str,
|
||||
post_json_object: {}, person_cache: {},
|
||||
translate: {}, announcing_actor: str,
|
||||
theme_name: str) -> None:
|
||||
theme_name: str,
|
||||
system_language: str, box_name: str) -> None:
|
||||
""" Generates a json file which can be used for TTS announcement
|
||||
of incoming inbox posts
|
||||
"""
|
||||
|
@ -554,5 +558,35 @@ def update_speaker(base_dir: str, http_prefix: str,
|
|||
post_json_object, person_cache,
|
||||
translate, announcing_actor,
|
||||
theme_name)
|
||||
speaker_filename = acct_dir(base_dir, nickname, domain) + '/speaker.json'
|
||||
if not speaker_json:
|
||||
return
|
||||
account_dir = acct_dir(base_dir, nickname, domain)
|
||||
speaker_filename = account_dir + '/speaker.json'
|
||||
save_json(speaker_json, speaker_filename)
|
||||
|
||||
# save the ssml
|
||||
cached_ssml_filename = \
|
||||
get_cached_post_filename(base_dir, nickname,
|
||||
domain, post_json_object)
|
||||
if not cached_ssml_filename:
|
||||
return
|
||||
cached_ssml_filename = cached_ssml_filename.replace('.html', '.ssml')
|
||||
if box_name == 'outbox':
|
||||
cached_ssml_filename = \
|
||||
cached_ssml_filename.replace('/postcache/', '/outbox/')
|
||||
gender = None
|
||||
if speaker_json.get('gender'):
|
||||
gender = speaker_json['gender']
|
||||
ssml_str = \
|
||||
_speaker_endpoint_ssml(speaker_json['name'],
|
||||
speaker_json['summary'],
|
||||
speaker_json['say'],
|
||||
speaker_json['imageDescription'],
|
||||
speaker_json['detectedLinks'],
|
||||
system_language,
|
||||
gender, box_name)
|
||||
try:
|
||||
with open(cached_ssml_filename, 'w+') as fp_ssml:
|
||||
fp_ssml.write(ssml_str)
|
||||
except OSError:
|
||||
print('EX: unable to write ssml ' + cached_ssml_filename)
|
||||
|
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 981 B After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 980 B After Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.0 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 6.9 KiB |
After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 8.0 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 980 B After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 1019 B After Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 7.6 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 8.1 KiB |
19
utils.py
|
@ -1745,6 +1745,25 @@ def delete_cached_html(base_dir: str, nickname: str, domain: str,
|
|||
'unable to delete cached post file ' +
|
||||
str(cached_post_filename))
|
||||
|
||||
cached_post_filename = cached_post_filename.replace('.html', '.ssml')
|
||||
if os.path.isfile(cached_post_filename):
|
||||
try:
|
||||
os.remove(cached_post_filename)
|
||||
except OSError:
|
||||
print('EX: delete_cached_html ' +
|
||||
'unable to delete cached ssml post file ' +
|
||||
str(cached_post_filename))
|
||||
|
||||
cached_post_filename = \
|
||||
cached_post_filename.replace('/postcache/', '/outbox/')
|
||||
if os.path.isfile(cached_post_filename):
|
||||
try:
|
||||
os.remove(cached_post_filename)
|
||||
except OSError:
|
||||
print('EX: delete_cached_html ' +
|
||||
'unable to delete cached outbox ssml post file ' +
|
||||
str(cached_post_filename))
|
||||
|
||||
|
||||
def _delete_hashtags_on_post(base_dir: str, post_json_object: {}) -> None:
|
||||
"""Removes hashtags when a post is deleted
|
||||
|
|
|
@ -14,6 +14,7 @@ from utils import get_config_param
|
|||
from utils import acct_dir
|
||||
from webapp_utils import html_header_with_external_style
|
||||
from webapp_utils import html_footer
|
||||
from webapp_utils import get_banner_file
|
||||
|
||||
|
||||
def load_access_keys_for_accounts(base_dir: str, key_shortcuts: {},
|
||||
|
@ -43,7 +44,7 @@ def html_access_keys(css_cache: {}, base_dir: str,
|
|||
nickname: str, domain: str,
|
||||
translate: {}, access_keys: {},
|
||||
default_access_keys: {},
|
||||
default_timeline: str) -> str:
|
||||
default_timeline: str, theme: str) -> str:
|
||||
"""Show and edit key shortcuts
|
||||
"""
|
||||
access_keys_filename = \
|
||||
|
@ -53,6 +54,9 @@ def html_access_keys(css_cache: {}, base_dir: str,
|
|||
if access_keys_from_file:
|
||||
access_keys = access_keys_from_file
|
||||
|
||||
timeline_key = access_keys['menuTimeline']
|
||||
submit_key = access_keys['submitButton']
|
||||
|
||||
access_keys_form = ''
|
||||
css_filename = base_dir + '/epicyon-profile.css'
|
||||
if os.path.isfile(base_dir + '/epicyon.css'):
|
||||
|
@ -62,6 +66,20 @@ def html_access_keys(css_cache: {}, base_dir: str,
|
|||
get_config_param(base_dir, 'instanceTitle')
|
||||
access_keys_form = \
|
||||
html_header_with_external_style(css_filename, instance_title, None)
|
||||
|
||||
access_keys_form += \
|
||||
'<header>\n' + \
|
||||
'<a href="/users/' + nickname + '/' + \
|
||||
default_timeline + '" title="' + \
|
||||
translate['Switch to timeline view'] + '" alt="' + \
|
||||
translate['Switch to timeline view'] + '">\n'
|
||||
banner_file, _ = \
|
||||
get_banner_file(base_dir, nickname, domain, theme)
|
||||
access_keys_form += '<img loading="lazy" decoding="async" ' + \
|
||||
'class="timeline-banner" alt="" ' + \
|
||||
'src="/users/' + nickname + '/' + banner_file + '" /></a>\n' + \
|
||||
'</header>\n'
|
||||
|
||||
access_keys_form += '<div class="container">\n'
|
||||
|
||||
access_keys_form += \
|
||||
|
@ -73,8 +91,6 @@ def html_access_keys(css_cache: {}, base_dir: str,
|
|||
access_keys_form += ' <form method="POST" action="' + \
|
||||
'/users/' + nickname + '/changeAccessKeys">\n'
|
||||
|
||||
timeline_key = access_keys['menuTimeline']
|
||||
submit_key = access_keys['submitButton']
|
||||
access_keys_form += \
|
||||
' <center>\n' + \
|
||||
' <button type="submit" class="button" ' + \
|
||||
|
|
|
@ -57,28 +57,42 @@ def header_buttons_timeline(default_timeline: str,
|
|||
if default_timeline == 'tlmedia':
|
||||
tl_str += \
|
||||
'<a href="' + users_path + '/tlmedia" tabindex="-1" ' + \
|
||||
'accesskey="' + access_keys['menuMedia'] + '"' + \
|
||||
'accesskey="' + access_keys['menuMedia'] + '"'
|
||||
if box_name == 'tlmedia':
|
||||
tl_str += ' aria-current="location"'
|
||||
tl_str += \
|
||||
'><button class="' + \
|
||||
mediaButton + '"><span>' + translate['Media'] + \
|
||||
'</span></button></a>'
|
||||
elif default_timeline == 'tlblogs':
|
||||
tl_str += \
|
||||
'<a href="' + users_path + \
|
||||
'/tlblogs" tabindex="-1"><button class="' + \
|
||||
'/tlblogs" tabindex="-1"'
|
||||
if box_name == 'tlblogs':
|
||||
tl_str += ' aria-current="location"'
|
||||
tl_str += \
|
||||
'><button class="' + \
|
||||
blogs_button + '"><span>' + translate['Blogs'] + \
|
||||
'</span></button></a>'
|
||||
elif default_timeline == 'tlfeatures':
|
||||
tl_str += \
|
||||
'<a href="' + users_path + \
|
||||
'/tlfeatures" tabindex="-1"><button class="' + \
|
||||
'/tlfeatures" tabindex="-1"'
|
||||
if box_name == 'tlfeatures':
|
||||
tl_str += ' aria-current="location"'
|
||||
tl_str += \
|
||||
'><button class="' + \
|
||||
features_button + '"><span>' + translate['Features'] + \
|
||||
'</span></button></a>'
|
||||
else:
|
||||
tl_str += \
|
||||
'<a href="' + users_path + \
|
||||
'/inbox" tabindex="-1"><button class="' + \
|
||||
inbox_button + '"><span>' + \
|
||||
translate['Inbox'] + '</span></button></a>'
|
||||
inbox_button + '"'
|
||||
if box_name == 'inbox':
|
||||
tl_str += ' aria-current="location"'
|
||||
tl_str += \
|
||||
'><span>' + translate['Inbox'] + '</span></button></a>'
|
||||
|
||||
# if this is a news instance and we are viewing the news timeline
|
||||
features_header = False
|
||||
|
@ -87,8 +101,11 @@ def header_buttons_timeline(default_timeline: str,
|
|||
|
||||
if not features_header:
|
||||
tl_str += \
|
||||
'<a href="' + users_path + \
|
||||
'/dm" tabindex="-1"><button class="' + dm_button + \
|
||||
'<a href="' + users_path + '/dm" tabindex="-1"'
|
||||
if box_name == 'dm':
|
||||
tl_str += ' aria-current="location"'
|
||||
tl_str += \
|
||||
'><button class="' + dm_button + \
|
||||
'"><span>' + html_highlight_label(translate['DM'], new_dm) + \
|
||||
'</span></button></a>'
|
||||
|
||||
|
@ -96,8 +113,11 @@ def header_buttons_timeline(default_timeline: str,
|
|||
acct_dir(base_dir, nickname, domain) + '/tlreplies.index'
|
||||
if os.path.isfile(replies_index_filename):
|
||||
tl_str += \
|
||||
'<a href="' + users_path + '/tlreplies" tabindex="-1">' + \
|
||||
'<button class="' + replies_button + '"><span>' + \
|
||||
'<a href="' + users_path + '/tlreplies" tabindex="-1"'
|
||||
if box_name == 'tlreplies':
|
||||
tl_str += ' aria-current="location"'
|
||||
tl_str += \
|
||||
'><button class="' + replies_button + '"><span>' + \
|
||||
html_highlight_label(translate['Replies'], new_reply) + \
|
||||
'</span></button></a>'
|
||||
|
||||
|
@ -106,15 +126,22 @@ def header_buttons_timeline(default_timeline: str,
|
|||
if not minimal and not features_header:
|
||||
tl_str += \
|
||||
'<a href="' + users_path + '/tlmedia" tabindex="-1" ' + \
|
||||
'accesskey="' + access_keys['menuMedia'] + '">' + \
|
||||
'<button class="' + \
|
||||
'accesskey="' + access_keys['menuMedia'] + '"'
|
||||
if box_name == 'tlmedia':
|
||||
tl_str += ' aria-current="location"'
|
||||
tl_str += \
|
||||
'><button class="' + \
|
||||
mediaButton + '"><span>' + translate['Media'] + \
|
||||
'</span></button></a>'
|
||||
else:
|
||||
if not minimal:
|
||||
tl_str += \
|
||||
'<a href="' + users_path + \
|
||||
'/inbox" tabindex="-1"><button class="' + \
|
||||
'/inbox" tabindex="-1"'
|
||||
if box_name == 'inbox':
|
||||
tl_str += ' aria-current="location"'
|
||||
tl_str += \
|
||||
'><button class="' + \
|
||||
inbox_button + '"><span>' + translate['Inbox'] + \
|
||||
'</span></button></a>'
|
||||
|
||||
|
@ -128,14 +155,22 @@ def header_buttons_timeline(default_timeline: str,
|
|||
title_str = translate['Article']
|
||||
tl_str += \
|
||||
'<a href="' + users_path + \
|
||||
'/tlblogs" tabindex="-1"><button class="' + \
|
||||
'/tlblogs" tabindex="-1"'
|
||||
if box_name == 'tlblogs':
|
||||
tl_str += ' aria-current="location"'
|
||||
tl_str += \
|
||||
'><button class="' + \
|
||||
blogs_button + '"><span>' + title_str + \
|
||||
'</span></button></a>'
|
||||
else:
|
||||
if not minimal:
|
||||
tl_str += \
|
||||
'<a href="' + users_path + \
|
||||
'/inbox" tabindex="-1"><button class="' + \
|
||||
'/inbox" tabindex="-1"'
|
||||
if box_name == 'inbox':
|
||||
tl_str += ' aria-current="location"'
|
||||
tl_str += \
|
||||
'><button class="' + \
|
||||
inbox_button + '"><span>' + translate['Inbox'] + \
|
||||
'</span></button></a>'
|
||||
|
||||
|
@ -145,7 +180,11 @@ def header_buttons_timeline(default_timeline: str,
|
|||
if not features_header:
|
||||
tl_str += \
|
||||
'<a href="' + users_path + \
|
||||
'/inbox" tabindex="-1"><button class="' + \
|
||||
'/inbox" tabindex="-1"'
|
||||
if box_name == 'inbox':
|
||||
tl_str += ' aria-current="location"'
|
||||
tl_str += \
|
||||
'><button class="' + \
|
||||
inbox_button + '"><span>' + translate['Inbox'] + \
|
||||
'</span></button></a>'
|
||||
|
||||
|
@ -205,7 +244,11 @@ def header_buttons_timeline(default_timeline: str,
|
|||
if not features_header:
|
||||
# button for the outbox
|
||||
tl_str += \
|
||||
'<a href="' + users_path + '/outbox"><button class="' + \
|
||||
'<a href="' + users_path + '/outbox"'
|
||||
if box_name == 'outbox':
|
||||
tl_str += ' aria-current="location"'
|
||||
tl_str += \
|
||||
'><button class="' + \
|
||||
sent_button + '" tabindex="-1"><span>' + translate['Sent'] + \
|
||||
'</span></button></a>'
|
||||
|
||||
|
@ -278,8 +321,11 @@ def header_buttons_timeline(default_timeline: str,
|
|||
|
||||
if features_header:
|
||||
tl_str += \
|
||||
'<a href="' + users_path + '/inbox" tabindex="-1">' + \
|
||||
'<button class="button">' + \
|
||||
'<a href="' + users_path + '/inbox" tabindex="-1"'
|
||||
if box_name == 'inbox':
|
||||
tl_str += ' aria-current="location"'
|
||||
tl_str += \
|
||||
'><button class="button">' + \
|
||||
'<span>' + translate['User'] + '</span></button></a>'
|
||||
|
||||
# the newswire button to show right column links
|
||||
|
|
|
@ -1698,7 +1698,8 @@ def individual_post_as_html(signing_priv_key_pem: str,
|
|||
nickname, domain, domain_full,
|
||||
post_json_object, person_cache,
|
||||
translate, post_json_object['actor'],
|
||||
theme_name)
|
||||
theme_name, system_language,
|
||||
box_name)
|
||||
with open(announce_filename + '.tts', 'w+') as ttsfile:
|
||||
ttsfile.write('\n')
|
||||
|
||||
|
|
|
@ -419,13 +419,21 @@ def _page_number_buttons(users_path: str, box_name: str,
|
|||
for page in range(min_page_number, max_page_number):
|
||||
if num_str:
|
||||
num_str += ' ⸻ '
|
||||
aria_page_str = ''
|
||||
page_str = str(page)
|
||||
if page == page_number:
|
||||
page_str = '<mark>' + str(page) + '</mark>'
|
||||
aria_page_str = ' aria-current="true"'
|
||||
num_str += \
|
||||
'<a href="' + users_path + '/' + box_name + '?page=' + \
|
||||
str(page) + '" class="pageslist">' + page_str + '</a>'
|
||||
return '<center>' + num_str + '</center>'
|
||||
str(page) + '" class="pageslist" ' + \
|
||||
'aria-label="Current Page, Page ' + str(page) + \
|
||||
'"' + aria_page_str + '>' + page_str + '</a>'
|
||||
return '<center>\n' + \
|
||||
' <nav role="navigation" aria-label="Pagination Navigation">\n' + \
|
||||
' ' + num_str + '\n' + \
|
||||
' </nav>\n' + \
|
||||
'</center>\n'
|
||||
|
||||
|
||||
def html_timeline(css_cache: {}, default_timeline: str,
|
||||
|
@ -656,8 +664,11 @@ def html_timeline(css_cache: {}, default_timeline: str,
|
|||
moderation_button_str = ''
|
||||
if moderator and not minimal:
|
||||
moderation_button_str = \
|
||||
'<a href="' + users_path + \
|
||||
'/moderation"><button class="' + \
|
||||
'<a href="' + users_path + '/moderation"'
|
||||
if box_name == 'moderation':
|
||||
moderation_button_str += ' aria-current="location"'
|
||||
moderation_button_str += \
|
||||
'><button class="' + \
|
||||
moderation_button + '"><span>' + \
|
||||
html_highlight_label(translate['Mod'], new_report) + \
|
||||
' </span></button></a>'
|
||||
|
@ -669,19 +680,30 @@ def html_timeline(css_cache: {}, default_timeline: str,
|
|||
events_button_str = ''
|
||||
if not minimal:
|
||||
shares_button_str = \
|
||||
'<a href="' + users_path + '/tlshares"><button class="' + \
|
||||
shares_button + '"><span>' + \
|
||||
'<a href="' + users_path + '/tlshares"'
|
||||
if box_name == 'tlshares':
|
||||
shares_button_str += ' aria-current="location"'
|
||||
shares_button_str += \
|
||||
'><button class="' + shares_button + '"><span>' + \
|
||||
html_highlight_label(translate['Shares'], new_share) + \
|
||||
'</span></button></a>'
|
||||
|
||||
wanted_button_str = \
|
||||
'<a href="' + users_path + '/tlwanted"><button class="' + \
|
||||
wanted_button + '"><span>' + \
|
||||
wanted_button + '"'
|
||||
if box_name == 'tlwanted':
|
||||
wanted_button_str += ' aria-current="location"'
|
||||
wanted_button_str += \
|
||||
'><span>' + \
|
||||
html_highlight_label(translate['Wanted'], new_wanted) + \
|
||||
'</span></button></a>'
|
||||
|
||||
bookmarks_button_str = \
|
||||
'<a href="' + users_path + '/tlbookmarks"><button class="' + \
|
||||
'<a href="' + users_path + '/tlbookmarks"'
|
||||
if box_name == 'tlbookmarks':
|
||||
bookmarks_button_str += ' aria-current="location"'
|
||||
bookmarks_button_str += \
|
||||
'><button class="' + \
|
||||
bookmarks_button + '"><span>' + translate['Bookmarks'] + \
|
||||
'</span></button></a>'
|
||||
|
||||
|
|