Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Latest version of image support for toot #319

Closed
wants to merge 85 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
c66bef1
Image support using the term-image widget
danschwarz Feb 23, 2023
39e9959
Fix issue where status images wouldn't display when entering thread
danschwarz Feb 23, 2023
63f6572
Use term-image's UrwidImageScreen rather than urwid.Screen
danschwarz Feb 23, 2023
59bedd3
Added can_render_pixels method
danschwarz Feb 23, 2023
7c3bb9c
Removed unnecessary code
danschwarz Feb 23, 2023
8c1dbb8
Check for pixel vs cell rendering and size images accordingly
danschwarz Feb 24, 2023
e8dfc7c
Render images with rounded corners
danschwarz Feb 24, 2023
fa3d65e
Merge branch 'ihabunek:master' into image11
danschwarz Feb 24, 2023
03b9710
Reducing duplicate code related to image display
danschwarz Feb 24, 2023
7235753
Reference header and avatar images rather than the _static versions
danschwarz Feb 25, 2023
1820ee0
Removed --256 color option, as it doesn't work anymore
danschwarz Feb 25, 2023
8b56a59
Updated for more future-proof testing of pixel rendering
danschwarz Feb 25, 2023
356eabd
Added term-image dependency
danschwarz Feb 25, 2023
bb4235e
Render custom server emoji within display names
danschwarz Feb 27, 2023
0931fdf
Simplified image scaling for cell-based rendering
danschwarz Feb 27, 2023
8bd7646
Make better use of screen real estate for wide/short images
danschwarz Feb 27, 2023
70aa614
Add custom emoji display to "boosted" display names
danschwarz Feb 27, 2023
fddd0a6
Image support using the term-image widget
danschwarz Feb 23, 2023
3cb4d96
Fix issue where status images wouldn't display when entering thread
danschwarz Feb 23, 2023
8ead569
Use term-image's UrwidImageScreen rather than urwid.Screen
danschwarz Feb 23, 2023
b8bb521
Added can_render_pixels method
danschwarz Feb 23, 2023
150a8b4
Removed unnecessary code
danschwarz Feb 23, 2023
39fdc41
Check for pixel vs cell rendering and size images accordingly
danschwarz Feb 24, 2023
a7b5a8d
Render images with rounded corners
danschwarz Feb 24, 2023
301e107
Reducing duplicate code related to image display
danschwarz Feb 24, 2023
3b5d7f4
Reference header and avatar images rather than the _static versions
danschwarz Feb 25, 2023
8d64365
Removed --256 color option, as it doesn't work anymore
danschwarz Feb 25, 2023
55f01c6
Updated for more future-proof testing of pixel rendering
danschwarz Feb 25, 2023
7e23f34
Added term-image dependency
danschwarz Feb 25, 2023
f7e4841
Render custom server emoji within display names
danschwarz Feb 27, 2023
c7abd7f
Simplified image scaling for cell-based rendering
danschwarz Feb 27, 2023
4b3a1a2
Make better use of screen real estate for wide/short images
danschwarz Feb 27, 2023
7c1b120
Add custom emoji display to "boosted" display names
danschwarz Feb 27, 2023
efd6eeb
Render custom server emoji within display names
danschwarz Feb 27, 2023
8b7b1bc
Simplified image scaling for cell-based rendering
danschwarz Feb 27, 2023
1d66dcf
Make better use of screen real estate for wide/short images
danschwarz Feb 27, 2023
7bbbc25
widget cleanup
danschwarz Mar 2, 2023
b0d1b3e
fix
danschwarz Mar 2, 2023
d831c77
Merge branch 'image11' of https://github.com/danschwarz/toot into ima…
danschwarz Mar 2, 2023
a08fa81
Added image preview for media that include a thumbnail image
danschwarz Mar 3, 2023
a5522ae
Basic support for viewing Status and Mention notifs as a timeline
danschwarz Feb 25, 2023
064e70f
Make _notif_timeline_generator more robust
danschwarz Feb 25, 2023
5ab524c
Minor tweaks
ihabunek Mar 1, 2023
a7056d8
Bump version, add changelog
ihabunek Mar 1, 2023
5d68d4e
Merge branch 'master' into image11
danschwarz Mar 4, 2023
06614dd
Merge branch 'image11' of https://github.com/danschwarz/toot into ima…
danschwarz Mar 5, 2023
eb62e1d
Merge branch 'master' into image11
danschwarz Mar 5, 2023
14ebbea
Updated to latest term-image which doesn't have UrwidImageJanitor
danschwarz Mar 6, 2023
8551aa7
Merge branch 'master' into image11
danschwarz Mar 9, 2023
23a87a7
Left justify images in the status detail pane
danschwarz Mar 9, 2023
2a61e46
Add support for custom instance domains
ihabunek Mar 7, 2023
9f31007
Fix tests
ihabunek Mar 7, 2023
de50ca4
Don't prompt for toot text if media is given
ihabunek Mar 8, 2023
1d6862b
Add poll options to toot post
ihabunek Mar 8, 2023
bbc2501
Fix sporadically failing test
ihabunek Mar 8, 2023
c3aaffb
Update changelog, bump version
ihabunek Mar 9, 2023
b51d720
Basic support for followed accounts in TUI
danschwarz Jan 3, 2023
ffe1785
Changed color of unfollowed accounts from grey to cyan (visiblity)
danschwarz Mar 11, 2023
45fd557
Merge branch 'master' into image11
danschwarz Mar 13, 2023
265c638
Fixed display of server custom emoji in certain cases
danschwarz Mar 13, 2023
b0ee03a
Changed color of unfollowed accounts from grey to cyan (visiblity)
danschwarz Mar 16, 2023
865c223
Changed color of unfollowed accounts from grey to cyan (visiblity)
danschwarz Mar 16, 2023
587a6c0
Add support for custom instance domains
ihabunek Mar 7, 2023
032e43b
Fix tests
ihabunek Mar 7, 2023
13bdacd
Add poll options to toot post
ihabunek Mar 8, 2023
9c42e1e
Restructured Goto Menu items for clarity, added error display
danschwarz Mar 16, 2023
4325e9d
Fixed refresh so it stays on the currently selected timeline
danschwarz Mar 16, 2023
0842a6f
Extract refresh timeline code
ihabunek Mar 16, 2023
dd99b77
Fixed display of server custom emoji in certain cases
danschwarz Mar 18, 2023
e8ffa1b
Round corners on images only when we render images to pixels
danschwarz Mar 18, 2023
30c3738
Merge branch 'image11' of https://github.com/danschwarz/toot into ima…
danschwarz Mar 19, 2023
a09f7a6
Pass the TUI object to Timeline in constructor
ihabunek Mar 15, 2023
f03ddb4
Remove no longer needed args to Timeline
ihabunek Mar 15, 2023
93ed651
Add type annotations
ihabunek Mar 15, 2023
83db087
Use direct invocation istead of signals
ihabunek Mar 15, 2023
0736103
Fix for boosting of statuses that were previously boosted by others
danschwarz Mar 17, 2023
6dc0839
Directly call async_load_images rather than rely on signals
danschwarz Mar 19, 2023
054528d
Merge branch 'master' into image11
danschwarz Mar 20, 2023
48c6c7a
locked to term-image 0.6.0 dependency
danschwarz Mar 30, 2023
d64175f
Merge branch 'master' into image11
danschwarz Mar 30, 2023
e5e8631
Merge branch 'master' into image11
danschwarz Apr 12, 2023
0251af7
Merge branch 'master' into image11
danschwarz Apr 26, 2023
e1d3a4a
Move to Python 3.7 as a minimum version
danschwarz Apr 28, 2023
0e8f4c9
Merge branch 'master' into image11
danschwarz May 28, 2023
58fa140
Upgraded to term-image 0.6.1
danschwarz May 31, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@ on: [push, pull_request]

jobs:
test:
# Older Ubuntu required for testing on Python 3.6 which is not available in
# later versions. Remove once support for 3.6 is dropped.
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
strategy:
matrix:
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11"]
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]

steps:
- uses: actions/checkout@v3
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@
/toot-*.tar.gz
debug.log
/pyrightconfig.json
/venv/
/book
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ requests>=2.13,<3.0
beautifulsoup4>=4.5.0,<5.0
wcwidth>=0.1.7
urwid>=2.0.0,<3.0

pillow>=9.5.0
term-image==0.6.1
4 changes: 3 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@
'Programming Language :: Python :: 3',
],
packages=['toot', 'toot.tui', 'toot.utils'],
python_requires=">=3.6",
python_requires=">=3.7",
install_requires=[
"requests>=2.13,<3.0",
"beautifulsoup4>=4.5.0,<5.0",
"wcwidth>=0.1.7",
"urwid>=2.0.0,<3.0",
"pillow>=9.5.0",
"term-image==0.6.1",
],
entry_points={
'console_scripts': [
Expand Down
31 changes: 30 additions & 1 deletion toot/tui/app.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging
import urwid
import requests

from concurrent.futures import ThreadPoolExecutor

Expand All @@ -16,6 +17,9 @@
from .poll import Poll
from .timeline import Timeline
from .utils import parse_content_links, show_media, copy_to_clipboard
from PIL import Image
from term_image.widget import UrwidImageScreen


logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -82,6 +86,7 @@ def create(cls, app, user, args):
loop = urwid.MainLoop(
tui,
palette=PALETTE,
screen=UrwidImageScreen(), # like urwid.raw_display.Screen, but clears Kitty + iTerm2 images on startup
event_loop=urwid.AsyncioEventLoop(),
unhandled_input=tui.unhandled_input,
)
Expand Down Expand Up @@ -236,8 +241,8 @@ def _close(*args):

self.connect_default_timeline_signals(timeline)
urwid.connect_signal(timeline, "close", _close)

self.body = timeline
timeline.refresh_status_details()
self.refresh_footer(timeline)

def async_load_timeline(self, is_initial, timeline_name=None, local=None):
Expand Down Expand Up @@ -636,6 +641,30 @@ def _done(loop):

return self.run_in_thread(_delete, done_callback=_done)

def async_load_image(self, timeline, status, path, placeholder_index):
def _load():
# don't bother loading images for statuses we are not viewing now
if timeline.get_focused_status().id != status.id:
return

if not hasattr(timeline, "images"):
timeline.images = dict()
try:
img = Image.open(requests.get(path, stream=True).raw)
if img.format == 'PNG' and img.mode != 'RGBA':
img = img.convert("RGBA")
timeline.images[str(hash(path))] = img
except: # noqa E722
pass # ignore errors; if we can't load an image, just show blank

def _done(loop):
# don't bother loading images for statuses we are not viewing now
if timeline.get_focused_status().id != status.id:
return
timeline.update_status_image(status, path, placeholder_index)

return self.run_in_thread(_load, done_callback=_done)

def copy_status(self, status):
# TODO: copy a better version of status content
# including URLs
Expand Down
56 changes: 48 additions & 8 deletions toot/tui/overlays.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import json
import requests
import traceback
import urwid
import webbrowser

from toot import __version__
from toot.utils import format_content
from .utils import highlight_hashtags, highlight_keys
from .widgets import Button, EditBox, SelectableText
from .utils import highlight_hashtags, highlight_keys, add_corners
from .widgets import Button, EditBox, SelectableText, EmojiText
from toot import api
from PIL import Image
from term_image.image import AutoImage
from term_image.widget import UrwidImage


class StatusSource(urwid.Padding):
Expand Down Expand Up @@ -249,6 +253,45 @@ def setup_listbox(self):
walker = urwid.SimpleListWalker(actions)
super().__init__(walker)

def account_header(self, account):
if account['avatar'] and not account["avatar"].endswith("missing.png"):
img = Image.open(requests.get(account['avatar'], stream=True).raw)

if img.format == 'PNG' and img.mode != 'RGBA':
img = img.convert("RGBA")
aimg = urwid.BoxAdapter(
UrwidImage(
AutoImage(
add_corners(img, 10)), upscale=True),
10)
else:
aimg = urwid.BoxAdapter(urwid.SolidFill(" "), 10)

if account['header'] and not account["header"].endswith("missing.png"):
img = Image.open(requests.get(account['header'], stream=True).raw)

if img.format == 'PNG' and img.mode != 'RGBA':
img = img.convert("RGBA")
himg = (urwid.BoxAdapter(
UrwidImage(
AutoImage(
add_corners(img, 10)
), upscale=True),
10)
)
else:
himg = urwid.BoxAdapter(urwid.SolidFill(" "), 10)

atxt = urwid.Pile([urwid.Divider(),
urwid.AttrMap(
EmojiText(account["display_name"], account["emojis"]),
"green"),
(urwid.Text(("yellow", "@" + self.account['acct'])))])
columns = urwid.Columns([aimg, ("weight", 9999, himg)], dividechars=2, min_width=20)

header = urwid.Pile([columns, urwid.Divider(), atxt])
return header

def generate_contents(self, account, relationship=None, last_action=None):
if self.last_action and not self.last_action.startswith("Confirm"):
yield Button(f"Confirm {self.last_action}", on_press=take_action, user_data=self)
Expand All @@ -270,17 +313,14 @@ def generate_contents(self, account, relationship=None, last_action=None):

yield urwid.Divider("─")
yield urwid.Divider()
yield urwid.Text([('green', f"@{account['acct']}"), f" {account['display_name']}"])

yield self.account_header(account)

if account["note"]:
yield urwid.Divider()
for line in format_content(account["note"]):
yield urwid.Text(highlight_hashtags(line, followed_tags=set()))

yield urwid.Divider()
yield urwid.Text(["ID: ", ("green", f"{account['id']}")])
yield urwid.Text(["Since: ", ("green", f"{account['created_at'][:10]}")])
yield urwid.Divider()
yield urwid.Divider()

if account["bot"]:
yield urwid.Text([("green", "Bot \N{robot face}")])
Expand Down
Loading