Added translate status feature (requires server side support)
This commit is contained in:
parent
f9fef1927c
commit
be5948bac8
|
@ -211,6 +211,10 @@ def unbookmark(app, user, status_id):
|
|||
return _status_action(app, user, status_id, 'unbookmark')
|
||||
|
||||
|
||||
def translate(app, user, status_id):
|
||||
return _status_action(app, user, status_id, 'translate')
|
||||
|
||||
|
||||
def context(app, user, status_id):
|
||||
url = '/api/v1/statuses/{}/context'.format(status_id)
|
||||
|
||||
|
|
|
@ -106,6 +106,7 @@ class TUI(urwid.Frame):
|
|||
self.timeline = None
|
||||
self.overlay = None
|
||||
self.exception = None
|
||||
self.can_translate = False
|
||||
|
||||
super().__init__(self.body, header=self.header, footer=self.footer)
|
||||
|
||||
|
@ -206,6 +207,7 @@ class TUI(urwid.Frame):
|
|||
urwid.connect_signal(timeline, "source", _source)
|
||||
urwid.connect_signal(timeline, "links", _links)
|
||||
urwid.connect_signal(timeline, "zoom", _zoom)
|
||||
urwid.connect_signal(timeline, "translate", self.async_translate)
|
||||
|
||||
def build_timeline(self, name, statuses, local):
|
||||
def _close(*args):
|
||||
|
@ -232,7 +234,7 @@ class TUI(urwid.Frame):
|
|||
self.loop.set_alarm_in(5, lambda *args: self.footer.clear_message())
|
||||
config.save_config(self.config)
|
||||
|
||||
timeline = Timeline(name, statuses)
|
||||
timeline = Timeline(name, statuses, self.can_translate)
|
||||
|
||||
self.connect_default_timeline_signals(timeline)
|
||||
urwid.connect_signal(timeline, "next", _next)
|
||||
|
@ -261,8 +263,8 @@ class TUI(urwid.Frame):
|
|||
statuses = ancestors + [status] + descendants
|
||||
focus = len(ancestors)
|
||||
|
||||
timeline = Timeline("thread", statuses, focus, is_thread=True)
|
||||
|
||||
timeline = Timeline("thread", statuses, self.can_translate, focus,
|
||||
is_thread=True)
|
||||
self.connect_default_timeline_signals(timeline)
|
||||
urwid.connect_signal(timeline, "close", _close)
|
||||
|
||||
|
@ -303,6 +305,11 @@ class TUI(urwid.Frame):
|
|||
Attempt to update max_toot_chars from instance data.
|
||||
Does not work on vanilla Mastodon, works on Pleroma.
|
||||
See: https://github.com/tootsuite/mastodon/issues/4915
|
||||
|
||||
Also attempt to update translation flag from instance
|
||||
data. Translation is only present on Mastodon 4+ servers
|
||||
where the administrator has enabled this feature.
|
||||
See: https://github.com/mastodon/mastodon/issues/19328
|
||||
"""
|
||||
def _load_instance():
|
||||
return api.get_instance(self.app.instance)
|
||||
|
@ -310,6 +317,17 @@ class TUI(urwid.Frame):
|
|||
def _done(instance):
|
||||
if "max_toot_chars" in instance:
|
||||
self.max_toot_chars = instance["max_toot_chars"]
|
||||
if "translation" in instance:
|
||||
# instance is advertising translation service
|
||||
self.can_translate = instance["translation"]["enabled"]
|
||||
else:
|
||||
if "version" in instance:
|
||||
# fallback check:
|
||||
# get the major version number of the server
|
||||
# this works for Mastodon and Pleroma version strings
|
||||
# Mastodon versions < 4 do not have translation service
|
||||
# Revisit this logic if Pleroma implements translation
|
||||
self.can_translate = int(instance["version"][0]) > 3
|
||||
|
||||
return self.run_in_thread(_load_instance, done_callback=_done)
|
||||
|
||||
|
@ -484,6 +502,37 @@ class TUI(urwid.Frame):
|
|||
done_callback=_done
|
||||
)
|
||||
|
||||
def async_translate(self, timeline, status):
|
||||
def _translate():
|
||||
logger.info("Translating {}".format(status))
|
||||
self.footer.set_message("Translating status {}".format(status.id))
|
||||
|
||||
try:
|
||||
response = api.translate(self.app, self.user, status.id)
|
||||
except:
|
||||
response = None
|
||||
finally:
|
||||
self.footer.clear_message()
|
||||
|
||||
return response
|
||||
|
||||
def _done(response):
|
||||
if response is not None:
|
||||
# Create a new Status that is translated
|
||||
new_data = status.data
|
||||
new_data["content"] = response["content"]
|
||||
new_data["detected_source_language"] = response["detected_source_language"]
|
||||
new_status = self.make_status(new_data)
|
||||
|
||||
timeline.update_status(new_status)
|
||||
self.footer.set_message(f"Translated status {status.id} from {response['detected_source_language']}")
|
||||
else:
|
||||
self.footer.set_error_message("Translate server error")
|
||||
|
||||
self.loop.set_alarm_in(5, lambda *args: self.footer.clear_message())
|
||||
|
||||
self.run_in_thread(_translate, done_callback=_done )
|
||||
|
||||
def async_delete_status(self, timeline, status):
|
||||
def _delete():
|
||||
api.delete_status(self.app, self.user, status.id)
|
||||
|
|
|
@ -164,6 +164,7 @@ class Help(urwid.Padding):
|
|||
yield urwid.Text(h(" [B] - Boost/unboost status"))
|
||||
yield urwid.Text(h(" [C] - Compose new status"))
|
||||
yield urwid.Text(h(" [F] - Favourite/unfavourite status"))
|
||||
yield urwid.Text(h(" [N] - Translate status, if possible"))
|
||||
yield urwid.Text(h(" [R] - Reply to current status"))
|
||||
yield urwid.Text(h(" [S] - Show text marked as sensitive"))
|
||||
yield urwid.Text(h(" [T] - Show status thread (replies)"))
|
||||
|
|
|
@ -28,19 +28,21 @@ class Timeline(urwid.Columns):
|
|||
"source", # Show status source
|
||||
"links", # Show status links
|
||||
"thread", # Show thread for status
|
||||
"translate", # Translate status
|
||||
"save", # Save current timeline
|
||||
"zoom", # Open status in scrollable popup window
|
||||
]
|
||||
|
||||
def __init__(self, name, statuses, focus=0, is_thread=False):
|
||||
def __init__(self, name, statuses, can_translate, focus=0, is_thread=False):
|
||||
self.name = name
|
||||
self.is_thread = is_thread
|
||||
self.statuses = statuses
|
||||
self.can_translate = can_translate
|
||||
self.status_list = self.build_status_list(statuses, focus=focus)
|
||||
try:
|
||||
self.status_details = StatusDetails(statuses[focus], is_thread)
|
||||
self.status_details = StatusDetails(statuses[focus], is_thread, can_translate)
|
||||
except IndexError:
|
||||
self.status_details = StatusDetails(None, is_thread)
|
||||
self.status_details = StatusDetails(None, is_thread, can_translate)
|
||||
|
||||
super().__init__([
|
||||
("weight", 40, self.status_list),
|
||||
|
@ -97,7 +99,7 @@ class Timeline(urwid.Columns):
|
|||
self.draw_status_details(status)
|
||||
|
||||
def draw_status_details(self, status):
|
||||
self.status_details = StatusDetails(status, self.is_thread)
|
||||
self.status_details = StatusDetails(status, self.is_thread, self.can_translate)
|
||||
self.contents[2] = urwid.Padding(self.status_details, left=1), ("weight", 60, False)
|
||||
|
||||
def keypress(self, size, key):
|
||||
|
@ -157,6 +159,11 @@ class Timeline(urwid.Columns):
|
|||
self._emit("links", status)
|
||||
return
|
||||
|
||||
if key in ("n", "N"):
|
||||
if self.can_translate:
|
||||
self._emit("translate", status)
|
||||
return
|
||||
|
||||
if key in ("t", "T"):
|
||||
self._emit("thread", status)
|
||||
return
|
||||
|
@ -228,7 +235,7 @@ class Timeline(urwid.Columns):
|
|||
|
||||
|
||||
class StatusDetails(urwid.Pile):
|
||||
def __init__(self, status, in_thread):
|
||||
def __init__(self, status, in_thread, can_translate=False):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
|
@ -239,6 +246,7 @@ class StatusDetails(urwid.Pile):
|
|||
Whether the status is rendered from a thread status list.
|
||||
"""
|
||||
self.in_thread = in_thread
|
||||
self.can_translate = can_translate
|
||||
reblogged_by = status.author if status and status.reblog else None
|
||||
widget_list = list(self.content_generator(status.original, reblogged_by)
|
||||
if status else ())
|
||||
|
@ -290,10 +298,14 @@ class StatusDetails(urwid.Pile):
|
|||
application = application.get("name")
|
||||
|
||||
yield ("pack", urwid.AttrWrap(urwid.Divider("-"), "gray"))
|
||||
|
||||
translated = status.data.get("detected_source_language")
|
||||
|
||||
yield ("pack", urwid.Text([
|
||||
("gray", "⤶ {} ".format(status.data["replies_count"])),
|
||||
("yellow" if status.reblogged else "gray", "♺ {} ".format(status.data["reblogs_count"])),
|
||||
("yellow" if status.favourited else "gray", "★ {}".format(status.data["favourites_count"])),
|
||||
("yellow" if translated else "gray", " · Translated from {} ".format(translated) if translated else ""),
|
||||
("gray", " · {}".format(application) if application else ""),
|
||||
]))
|
||||
|
||||
|
@ -310,6 +322,7 @@ class StatusDetails(urwid.Pile):
|
|||
"[R]eply",
|
||||
"So[u]rce",
|
||||
"[Z]oom",
|
||||
"Tra[n]slate" if self.can_translate else "",
|
||||
"[H]elp",
|
||||
]
|
||||
options = " ".join(o for o in options if o)
|
||||
|
|
Loading…
Reference in New Issue