diff --git a/docs/settings.md b/docs/settings.md index baff676..c989fd3 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -51,6 +51,21 @@ visibility = "unlisted" scheduled_in = "30 minutes" ``` +## TUI view images + +> Introduced in toot 0.39.0 + +You can view images in a toot using an external program by setting the +`tui.media_viewer` option to your desired image viewer. When a toot is focused, +pressing `m` will launch the specified executable giving one or more URLs as +arguments. This works well with image viewers like `feh` which accept URLs as +arguments. + +```toml +[tui] +media_viewer = "feh" +``` + ## TUI color palette TUI uses Urwid which provides several color modes. See diff --git a/toot/tui/app.py b/toot/tui/app.py index 88a209e..349322c 100644 --- a/toot/tui/app.py +++ b/toot/tui/app.py @@ -1,4 +1,5 @@ import logging +import subprocess import urwid from concurrent.futures import ThreadPoolExecutor @@ -14,7 +15,7 @@ from .overlays import ExceptionStackTrace, GotoMenu, Help, StatusSource, StatusL from .overlays import StatusDeleteConfirmation, Account from .poll import Poll from .timeline import Timeline -from .utils import get_max_toot_chars, parse_content_links, show_media, copy_to_clipboard +from .utils import get_max_toot_chars, parse_content_links, copy_to_clipboard logger = logging.getLogger(__name__) @@ -138,6 +139,7 @@ class TUI(urwid.Frame): self.can_translate = False self.account = None self.followed_accounts = [] + self.media_viewer = settings.get_setting("tui.media_viewer", str) super().__init__(self.body, header=self.header, footer=self.footer) @@ -498,8 +500,13 @@ class TUI(urwid.Frame): def show_media(self, status): urls = [m["url"] for m in status.original.data["media_attachments"]] - if urls: - show_media(urls) + if not urls: + return + + if self.media_viewer: + subprocess.run([self.media_viewer] + urls) + else: + self.footer.set_error_message("Media viewer not configured") def show_context_menu(self, status): # TODO: show context menu diff --git a/toot/tui/timeline.py b/toot/tui/timeline.py index 93421ce..60dfeb0 100644 --- a/toot/tui/timeline.py +++ b/toot/tui/timeline.py @@ -95,6 +95,7 @@ class Timeline(urwid.Columns): return None poll = status.original.data.get("poll") + show_media = status.original.data["media_attachments"] and self.tui.media_viewer options = [ "[A]ccount" if not status.is_mine else "", @@ -105,6 +106,8 @@ class Timeline(urwid.Columns): "[V]iew", "[T]hread" if not self.is_thread else "", "L[i]nks", + "[M]edia" if show_media else "", + self.tui.media_viewer, "[R]eply", "[P]oll" if poll and not poll["expired"] else "", "So[u]rce", diff --git a/toot/tui/utils.py b/toot/tui/utils.py index 734ae32..0eb9bff 100644 --- a/toot/tui/utils.py +++ b/toot/tui/utils.py @@ -1,12 +1,12 @@ import base64 import re -import shutil -import subprocess import urwid -from functools import reduce +from functools import lru_cache, reduce from html.parser import HTMLParser -from typing import List +from typing import List, Optional + +from toot import settings HASHTAG_PATTERN = re.compile(r'(?