mirror of
https://github.com/ihabunek/toot.git
synced 2025-04-18 00:48:47 -04:00
Merge branch 'ihabunek:master' into master
This commit is contained in:
commit
09980470d2
2
.flake8
2
.flake8
@ -1,4 +1,4 @@
|
||||
[flake8]
|
||||
exclude=build,tests,tmp,venv,toot/tui/scroll.py
|
||||
exclude=build,tests,tmp,venv,_env,toot/tui/scroll.py
|
||||
ignore=E128,W503,W504
|
||||
max-line-length=120
|
||||
|
14
CHANGELOG.md
14
CHANGELOG.md
@ -3,6 +3,20 @@ Changelog
|
||||
|
||||
<!-- Do not edit. This file is automatically generated from changelog.yaml.-->
|
||||
|
||||
**0.44.1 (2024-08-12)**
|
||||
|
||||
* Make it possible to pass status URL as status_id, experimental (thanks
|
||||
@nemobis)
|
||||
* Show statuses in search results (thanks @nemobis)
|
||||
|
||||
**0.44.0 (2024-08-12)**
|
||||
|
||||
* **BREAKING:** Require Python 3.8+
|
||||
* Add `toot diag` for displaying diagnostic info (thanks Dan Schwarz)
|
||||
* TUI: Improve image support (thanks @AnonymouX47)
|
||||
* TUI: Add support for indexed color image rendering (#483) (thanks Dan Schwarz)
|
||||
* TUI: Fix crash bug (#483) (thanks Dan Schwarz)
|
||||
|
||||
**0.43.0 (2024-04-13)**
|
||||
|
||||
* TUI: Support displaying images (thanks Dan Schwarz)
|
||||
|
@ -37,11 +37,18 @@ Terminal User Interface
|
||||
|
||||
toot includes a terminal user interface (TUI). Run it with ``toot tui``.
|
||||
|
||||
TUI Features:
|
||||
-------------
|
||||
|
||||
* Block graphic image display (requires optional libraries `pillow <https://pypi.org/project/pillow/>`, `term-image <https://pypi.org/project/term-image/>`, and `urwidgets <https://pypi.org/project/urwidgets/>`)
|
||||
* Bitmapped image display in `kitty <https://sw.kovidgoyal.net/kitty/>` terminal ``toot tui -f kitty``
|
||||
* Bitmapped image display in `iTerm2 <https://iterm2.com/>`, or `WezTerm <https://wezfurlong.org/wezterm/index.html>` terminal ``toot tui -f iterm``
|
||||
|
||||
|
||||
.. image :: https://raw.githubusercontent.com/ihabunek/toot/master/docs/images/tui_list.png
|
||||
|
||||
.. image :: https://raw.githubusercontent.com/ihabunek/toot/master/docs/images/tui_compose.png
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
|
@ -1,7 +1,17 @@
|
||||
0.44.1:
|
||||
date: 2024-08-12
|
||||
changes:
|
||||
- "Make it possible to pass status URL as status_id, experimental (thanks @nemobis)"
|
||||
- "Show statuses in search results (thanks @nemobis)"
|
||||
|
||||
0.44.0:
|
||||
date: TBA
|
||||
date: 2024-08-12
|
||||
changes:
|
||||
- "**BREAKING:** Require Python 3.8+"
|
||||
- "Add `toot diag` for displaying diagnostic info (thanks Dan Schwarz)"
|
||||
- "TUI: Improve image support (thanks @AnonymouX47)"
|
||||
- "TUI: Add support for indexed color image rendering (#483) (thanks Dan Schwarz)"
|
||||
- "TUI: Fix crash bug (#483) (thanks Dan Schwarz)"
|
||||
|
||||
0.43.0:
|
||||
date: 2024-04-13
|
||||
|
@ -3,6 +3,20 @@ Changelog
|
||||
|
||||
<!-- Do not edit. This file is automatically generated from changelog.yaml.-->
|
||||
|
||||
**0.44.1 (2024-08-12)**
|
||||
|
||||
* Make it possible to pass status URL as status_id, experimental (thanks
|
||||
@nemobis)
|
||||
* Show statuses in search results (thanks @nemobis)
|
||||
|
||||
**0.44.0 (2024-08-12)**
|
||||
|
||||
* **BREAKING:** Require Python 3.8+
|
||||
* Add `toot diag` for displaying diagnostic info (thanks Dan Schwarz)
|
||||
* TUI: Improve image support (thanks @AnonymouX47)
|
||||
* TUI: Add support for indexed color image rendering (#483) (thanks Dan Schwarz)
|
||||
* TUI: Fix crash bug (#483) (thanks Dan Schwarz)
|
||||
|
||||
**0.43.0 (2024-04-13)**
|
||||
|
||||
* TUI: Support displaying images (thanks Dan Schwarz)
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 237 KiB |
Binary file not shown.
Before Width: | Height: | Size: 192 KiB After Width: | Height: | Size: 617 KiB |
Binary file not shown.
Before Width: | Height: | Size: 209 KiB |
@ -81,5 +81,4 @@ packages=[
|
||||
[tool.setuptools_scm]
|
||||
|
||||
[tool.pyright]
|
||||
typeCheckingMode = "strict"
|
||||
pythonVersion = "3.8"
|
24
toot/api.py
24
toot/api.py
@ -44,10 +44,34 @@ def _account_action(app, user, account, action) -> Response:
|
||||
|
||||
|
||||
def _status_action(app, user, status_id, action, data=None) -> Response:
|
||||
status_id = _resolve_status_id(app, user, status_id)
|
||||
url = f"/api/v1/statuses/{status_id}/{action}"
|
||||
return http.post(app, user, url, data=data)
|
||||
|
||||
|
||||
def _resolve_status_id(app, user, id_or_url) -> str:
|
||||
"""
|
||||
If given an URL instead of status ID, attempt to resolve the status ID.
|
||||
|
||||
TODO: Not 100% sure this is the correct way of doing this, but it seems to
|
||||
work for all test cases I've thrown at it. So leaving it undocumented until
|
||||
we're happy it works.
|
||||
"""
|
||||
if re.match(r"^https?://", id_or_url):
|
||||
response = search(app, user, id_or_url, resolve=True, type="statuses")
|
||||
statuses = response.json().get("statuses")
|
||||
|
||||
if not statuses:
|
||||
raise ConsoleError(f"Cannot find status matching URL {id_or_url}")
|
||||
|
||||
if len(statuses) > 1:
|
||||
raise ConsoleError(f"Found multiple statuses mathcing URL {id_or_url}")
|
||||
|
||||
return statuses[0]["id"]
|
||||
|
||||
return id_or_url
|
||||
|
||||
|
||||
def _tag_action(app, user, tag_name, action) -> Response:
|
||||
url = f"/api/v1/tags/{tag_name}/{action}"
|
||||
return http.post(app, user, url)
|
||||
|
@ -173,6 +173,7 @@ def cli(ctx: click.Context, max_width: int, color: bool, debug: bool, as_user: s
|
||||
|
||||
from toot.cli import accounts # noqa
|
||||
from toot.cli import auth # noqa
|
||||
from toot.cli import diag # noqa
|
||||
from toot.cli import lists # noqa
|
||||
from toot.cli import post # noqa
|
||||
from toot.cli import read # noqa
|
||||
|
30
toot/cli/diag.py
Normal file
30
toot/cli/diag.py
Normal file
@ -0,0 +1,30 @@
|
||||
from typing import Optional
|
||||
import click
|
||||
from toot import api, config
|
||||
from toot.entities import Data
|
||||
from toot.output import print_diags
|
||||
from toot.cli import cli
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option(
|
||||
"-f",
|
||||
"--files",
|
||||
is_flag=True,
|
||||
help="Print contents of the config and settings files in diagnostic output",
|
||||
)
|
||||
@click.option(
|
||||
"-s",
|
||||
"--server",
|
||||
is_flag=True,
|
||||
help="Print information about the curren server in diagnostic output",
|
||||
)
|
||||
def diag(files: bool, server: bool):
|
||||
"""Display useful information for diagnosing problems"""
|
||||
instance_dict: Optional[Data] = None
|
||||
if server:
|
||||
_, app = config.get_active_user_app()
|
||||
if app:
|
||||
instance_dict = api.get_instance(app.base_url).json()
|
||||
|
||||
print_diags(instance_dict, files)
|
@ -9,6 +9,8 @@ from typing import BinaryIO, Optional, Tuple
|
||||
from toot import api, config
|
||||
from toot.cli import AccountParamType, cli, json_option, pass_context, Context
|
||||
from toot.cli import DURATION_EXAMPLES, VISIBILITY_CHOICES
|
||||
from toot.tui.constants import VISIBILITY_OPTIONS # move to top-level ?
|
||||
|
||||
from toot.cli.validators import validate_duration, validate_language
|
||||
from toot.entities import MediaAttachment, from_dict
|
||||
from toot.utils import EOF_KEY, delete_tmp_status_file, editor_input, multiline_input
|
||||
@ -38,7 +40,10 @@ from toot.utils.datetime import parse_datetime
|
||||
)
|
||||
@click.option(
|
||||
"--visibility", "-v",
|
||||
help="Post visibility",
|
||||
help="Post visibility: " + "; "
|
||||
.join("{} = {}".format(visibility, description)
|
||||
for visibility, caption, description in VISIBILITY_OPTIONS),
|
||||
default=VISIBILITY_CHOICES[0],
|
||||
type=click.Choice(VISIBILITY_CHOICES),
|
||||
)
|
||||
@click.option(
|
||||
|
@ -1,13 +1,18 @@
|
||||
import click
|
||||
import platform
|
||||
import re
|
||||
import shutil
|
||||
import textwrap
|
||||
import typing as t
|
||||
|
||||
from toot.entities import Account, Instance, Notification, Poll, Status, List
|
||||
from datetime import datetime, timezone
|
||||
from importlib.metadata import version
|
||||
from wcwidth import wcswidth
|
||||
|
||||
from toot import __version__, config, settings
|
||||
from toot.entities import Account, Data, Instance, Notification, Poll, Status, List
|
||||
from toot.utils import get_text, html_to_paragraphs
|
||||
from toot.wcstring import wc_wrap
|
||||
from wcwidth import wcswidth
|
||||
|
||||
|
||||
DEFAULT_WIDTH = 80
|
||||
@ -154,8 +159,9 @@ def print_list_accounts(accounts):
|
||||
|
||||
|
||||
def print_search_results(results):
|
||||
accounts = results["accounts"]
|
||||
hashtags = results["hashtags"]
|
||||
accounts = results.get("accounts")
|
||||
hashtags = results.get("hashtags")
|
||||
statuses = results.get("statuses")
|
||||
|
||||
if accounts:
|
||||
click.echo("\nAccounts:")
|
||||
@ -165,7 +171,12 @@ def print_search_results(results):
|
||||
click.echo("\nHashtags:")
|
||||
click.echo(", ".join([format_tag_name(tag) for tag in hashtags]))
|
||||
|
||||
if not accounts and not hashtags:
|
||||
if statuses:
|
||||
click.echo("\nStatuses:")
|
||||
for status in statuses:
|
||||
click.echo(f" * {green(status['id'])} {status['url']}")
|
||||
|
||||
if not accounts and not hashtags and not statuses:
|
||||
click.echo("Nothing found")
|
||||
|
||||
|
||||
@ -314,6 +325,78 @@ def format_account_name(account: Account) -> str:
|
||||
return acct
|
||||
|
||||
|
||||
def print_diags(instance_dict: t.Optional[Data], include_files: bool):
|
||||
click.echo(f'{green("## Toot Diagnostics")}')
|
||||
click.echo()
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
click.echo(f'{green("Current Date/Time:")} {now.strftime("%Y-%m-%d %H:%M:%S %Z")}')
|
||||
|
||||
click.echo(f'{green("Toot version:")} {__version__}')
|
||||
click.echo(f'{green("Platform:")} {platform.platform()}')
|
||||
|
||||
# print distro - only call if available (python 3.10+)
|
||||
fd_os_release = getattr(platform, "freedesktop_os_release", None) # novermin
|
||||
if callable(fd_os_release): # novermin
|
||||
try:
|
||||
name = platform.freedesktop_os_release()['PRETTY_NAME']
|
||||
click.echo(f'{green("Distro:")} {name}')
|
||||
except: # noqa
|
||||
pass
|
||||
|
||||
click.echo(f'{green("Python version:")} {platform.python_version()}')
|
||||
click.echo()
|
||||
|
||||
click.echo(green("Dependency versions:"))
|
||||
|
||||
deps = sorted(['beautifulsoup4', 'click', 'requests', 'tomlkit', 'urwid', 'wcwidth',
|
||||
'pillow', 'term-image', 'urwidgets', 'flake8', 'pytest', 'setuptools',
|
||||
'vermin', 'typing-extensions'])
|
||||
|
||||
for dep in deps:
|
||||
try:
|
||||
ver = version(dep)
|
||||
except: # noqa
|
||||
ver = yellow("not installed")
|
||||
|
||||
click.echo(f" * {dep}: {ver}")
|
||||
click.echo()
|
||||
|
||||
click.echo(f'{green("Settings file path:")} {settings.get_settings_path()}')
|
||||
click.echo(f'{green("Config file path:")} {config.get_config_file_path()}')
|
||||
|
||||
if instance_dict:
|
||||
click.echo(f'{green("Server URI:")} {instance_dict.get("uri")}')
|
||||
click.echo(f'{green("Server version:")} {instance_dict.get("version")}')
|
||||
|
||||
if include_files:
|
||||
click.echo(f'{green("Settings file contents:")}')
|
||||
try:
|
||||
with open(settings.get_settings_path(), 'r') as f:
|
||||
print("```toml")
|
||||
print(f.read())
|
||||
print("```")
|
||||
except: # noqa
|
||||
click.echo(f'{yellow("Could not open settings file")}')
|
||||
click.echo()
|
||||
|
||||
click.echo(f'{green("Config file contents:")}')
|
||||
click.echo("```json")
|
||||
try:
|
||||
with open(config.get_config_file_path(), 'r') as f:
|
||||
for line in f:
|
||||
# Do not output client secret or access token lines
|
||||
if "client_" in line or "token" in line:
|
||||
click.echo(f'{yellow("***CONTENTS REDACTED***")}')
|
||||
else:
|
||||
click.echo(line, nl=False)
|
||||
click.echo()
|
||||
|
||||
except: # noqa
|
||||
click.echo(f'{yellow("Could not open config file")}')
|
||||
click.echo("```")
|
||||
|
||||
|
||||
# Shorthand functions for coloring output
|
||||
|
||||
def blue(text: t.Any) -> str:
|
||||
|
@ -484,7 +484,8 @@ class StatusDetails(urwid.Pile):
|
||||
yield self.image_widget(m["url"], aspect=aspect)
|
||||
yield urwid.Divider()
|
||||
# video media may include a preview URL, show that as a fallback
|
||||
elif m["preview_url"].lower().endswith(('.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp')):
|
||||
elif m["preview_url"]:
|
||||
if m["preview_url"].lower().endswith(('.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp')):
|
||||
yield urwid.Text("")
|
||||
try:
|
||||
aspect = float(m["meta"]["small"]["aspect"])
|
||||
|
Loading…
x
Reference in New Issue
Block a user