This repository was archived by the owner on May 9, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 83
feat: rewrite bash preview scripts in python #168
Merged
Merged
Changes from all commits
Commits
Show all changes
50 commits
Select commit
Hold shift + click to select a range
9f5c895
chore: temporarily relocate initial bash preview scripts to old folder
Benexl 515660b
feat: implement the main preview text logic in python
Benexl 1d129a5
fix: remove extra bracket
Benexl 9a0bb65
feat: implement image preview
Benexl 7401a1a
feat: prefer to use direct implementation of graphics protocol over e…
Benexl 925c30c
fix: typo should be text not info
Benexl 44b3663
feat: grp studio, synonymns and tags separately for better ui / ux
Benexl 106278e
feat: improve synopsis separator styling
Benexl 097db71
feat: refactor ruling logic to function
Benexl e37f921
feat: include romaji title in synonymns if not already there
Benexl 2d8c1d3
feat: remove colon for better ui
Benexl 1928183
feat: next episode should come last in its grp for better ui ux
Benexl 0c3a963
feat: use ?? where episodes are unknown
Benexl 9a619b4
feat: use prefix in preview-script.py filename
Benexl 1519c8b
feat: create the preview script in the cache/preview dir
Benexl 29ce664
Merge remote-tracking branch 'origin/master' into feature/preview-scr…
Benexl a7b0f21
feat: rename info.py to media_info.py
Benexl 6e287d3
feat: rewrite episode info script in python
Benexl 8440ffb
feat: add a key for extra uniqueness
Benexl 6409320
feat: create temp episode preview script
Benexl 08ae878
feat: sanitize " in key
Benexl 23ebff3
fix: add .py extension to final path
Benexl e8387f3
feat: character previews in python
Benexl 6ccd96d
feat: review previews in python
Benexl 5193df2
feat: airing schedule previews in python
Benexl 393b9e6
feat: use actual file for preview script
Benexl 9050dd7
feat: disable image for character, review, airing-schedule
Benexl 091edb3
fix: remove extra bracket
Benexl a70db61
style: remove unnecessary comment
Benexl 25a46bd
feat: disable airing schedule preview
Benexl 76c1dcd
fix: specifying extension when saving file
Benexl f27c0b8
fix: order of operations
Benexl bd9bf24
feat: add more image render options
Benexl 5237668
feat: implement other image renders
Benexl 901d1e8
feat: rewrite FZF preview scripts to use ANSI utilities for improved …
Benexl 26bc84e
fix: clean up whitespace in ANSI utilities and preview script
Benexl 803c831
fix: improve value alignment in print_table_row for better formatting
Benexl 1f72e0a
feat: enhance display width calculation for better text alignment in …
Benexl f4958cc
fix: clean up whitespace in display_width and print_table_row functions
Benexl c8c4e1b
feat: refactor terminal width handling in FZF scripts for improved co…
Benexl 80771f6
feat: dynamic search rewrite in python
Benexl 725754e
feat: improve text display for dynamic search
Benexl 7b9de86
chore: cleanup old preview scripts
Benexl ece1f77
Merge branch 'master' into feature/preview-scripts-rewrite-to-python
Benexl 3b00869
style: remove unused imports
Benexl 6b8dfba
fix: remove double quotes
Benexl 54233ac
feat: remove redundancy and stick to ansi_utils
Benexl d38dc31
feat: export ansi utils to preview root dir when doing dynamic previews
Benexl 41aaf92
style: remove unused import
Benexl bf06d7e
Update viu_media/assets/scripts/fzf/media_info.py
Benexl File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,202 @@ | ||
| """ | ||
| ANSI utilities for FZF preview scripts. | ||
|
|
||
| Lightweight stdlib-only utilities to replace Rich dependency in preview scripts. | ||
| Provides RGB color formatting, table rendering, and markdown stripping. | ||
| """ | ||
|
|
||
| import os | ||
| import re | ||
| import shutil | ||
| import textwrap | ||
| import unicodedata | ||
|
|
||
|
|
||
| def get_terminal_width() -> int: | ||
| """ | ||
| Get terminal width, prioritizing FZF preview environment variables. | ||
|
|
||
| Returns: | ||
| Terminal width in columns | ||
| """ | ||
| fzf_cols = os.environ.get("FZF_PREVIEW_COLUMNS") | ||
| if fzf_cols: | ||
| return int(fzf_cols) | ||
| return shutil.get_terminal_size((80, 24)).columns | ||
|
|
||
|
|
||
| def display_width(text: str) -> int: | ||
| """ | ||
| Calculate the actual display width of text, accounting for wide characters. | ||
|
|
||
| Args: | ||
| text: Text to measure | ||
|
|
||
| Returns: | ||
| Display width in terminal columns | ||
| """ | ||
| width = 0 | ||
| for char in text: | ||
| # East Asian Width property: 'F' (Fullwidth) and 'W' (Wide) take 2 columns | ||
| if unicodedata.east_asian_width(char) in ("F", "W"): | ||
| width += 2 | ||
| else: | ||
| width += 1 | ||
| return width | ||
|
|
||
|
|
||
| def rgb_color(r: int, g: int, b: int, text: str, bold: bool = False) -> str: | ||
| """ | ||
| Format text with RGB color using ANSI escape codes. | ||
|
|
||
| Args: | ||
| r: Red component (0-255) | ||
| g: Green component (0-255) | ||
| b: Blue component (0-255) | ||
| text: Text to colorize | ||
| bold: Whether to make text bold | ||
|
|
||
| Returns: | ||
| ANSI-escaped colored text | ||
| """ | ||
| color_code = f"\x1b[38;2;{r};{g};{b}m" | ||
| bold_code = "\x1b[1m" if bold else "" | ||
| reset = "\x1b[0m" | ||
| return f"{color_code}{bold_code}{text}{reset}" | ||
|
|
||
|
|
||
| def parse_color(color_csv: str) -> tuple[int, int, int]: | ||
| """ | ||
| Parse RGB color from comma-separated string. | ||
|
|
||
| Args: | ||
| color_csv: Color as 'R,G,B' string | ||
|
|
||
| Returns: | ||
| Tuple of (r, g, b) integers | ||
| """ | ||
| parts = color_csv.split(",") | ||
| return int(parts[0]), int(parts[1]), int(parts[2]) | ||
|
|
||
|
|
||
| def print_rule(sep_color: str) -> None: | ||
| """ | ||
| Print a horizontal rule line. | ||
|
|
||
| Args: | ||
| sep_color: Color as 'R,G,B' string | ||
| """ | ||
| width = get_terminal_width() | ||
| r, g, b = parse_color(sep_color) | ||
| print(rgb_color(r, g, b, "─" * width)) | ||
|
|
||
|
|
||
| def print_table_row( | ||
| key: str, value: str, header_color: str, key_width: int, value_width: int | ||
| ) -> None: | ||
| """ | ||
| Print a two-column table row with left-aligned key and right-aligned value. | ||
|
|
||
| Args: | ||
| key: Left column text (header/key) | ||
| value: Right column text (value) | ||
| header_color: Color for key as 'R,G,B' string | ||
| key_width: Width for key column | ||
| value_width: Width for value column | ||
| """ | ||
| r, g, b = parse_color(header_color) | ||
| key_styled = rgb_color(r, g, b, key, bold=True) | ||
|
|
||
| # Get actual terminal width | ||
| term_width = get_terminal_width() | ||
|
|
||
| # Calculate display widths accounting for wide characters | ||
| key_display_width = display_width(key) | ||
|
|
||
| # Calculate actual value width based on terminal and key display width | ||
| actual_value_width = max(20, term_width - key_display_width - 2) | ||
|
|
||
| # Wrap value if it's too long (use character count, not display width for wrapping) | ||
| value_lines = textwrap.wrap(str(value), width=actual_value_width) if value else [""] | ||
|
|
||
| if not value_lines: | ||
| value_lines = [""] | ||
|
|
||
| # Print first line with properly aligned value | ||
| first_line = value_lines[0] | ||
| first_line_display_width = display_width(first_line) | ||
|
|
||
| # Use manual spacing to right-align based on display width | ||
| spacing = term_width - key_display_width - first_line_display_width - 2 | ||
| if spacing > 0: | ||
| print(f"{key_styled} {' ' * spacing}{first_line}") | ||
| else: | ||
| print(f"{key_styled} {first_line}") | ||
|
|
||
| # Print remaining wrapped lines (left-aligned, indented) | ||
| for line in value_lines[1:]: | ||
| print(f"{' ' * (key_display_width + 2)}{line}") | ||
|
|
||
|
|
||
| def strip_markdown(text: str) -> str: | ||
| """ | ||
| Strip markdown formatting from text. | ||
|
|
||
| Removes: | ||
| - Headers (# ## ###) | ||
| - Bold (**text** or __text__) | ||
| - Italic (*text* or _text_) | ||
| - Links ([text](url)) | ||
| - Code blocks (```code```) | ||
| - Inline code (`code`) | ||
|
|
||
| Args: | ||
| text: Markdown-formatted text | ||
|
|
||
| Returns: | ||
| Plain text with markdown removed | ||
| """ | ||
| if not text: | ||
| return "" | ||
|
|
||
| # Remove code blocks first | ||
| text = re.sub(r"```[\s\S]*?```", "", text) | ||
|
|
||
| # Remove inline code | ||
| text = re.sub(r"`([^`]+)`", r"\1", text) | ||
|
|
||
| # Remove headers | ||
| text = re.sub(r"^#{1,6}\s+", "", text, flags=re.MULTILINE) | ||
|
|
||
| # Remove bold (** or __) | ||
| text = re.sub(r"\*\*(.+?)\*\*", r"\1", text) | ||
| text = re.sub(r"__(.+?)__", r"\1", text) | ||
|
|
||
| # Remove italic (* or _) | ||
| text = re.sub(r"\*(.+?)\*", r"\1", text) | ||
| text = re.sub(r"_(.+?)_", r"\1", text) | ||
|
|
||
| # Remove links, keep text | ||
| text = re.sub(r"\[(.+?)\]\(.+?\)", r"\1", text) | ||
|
|
||
| # Remove images | ||
| text = re.sub(r"!\[.*?\]\(.+?\)", "", text) | ||
|
|
||
| return text.strip() | ||
|
|
||
|
|
||
| def wrap_text(text: str, width: int | None = None) -> str: | ||
|
||
| """ | ||
| Wrap text to terminal width. | ||
|
|
||
| Args: | ||
| text: Text to wrap | ||
| width: Width to wrap to (defaults to terminal width) | ||
|
|
||
| Returns: | ||
| Wrapped text | ||
| """ | ||
| if width is None: | ||
| width = get_terminal_width() | ||
|
|
||
| return textwrap.fill(text, width=width) | ||
22 changes: 0 additions & 22 deletions
22
viu_media/assets/scripts/fzf/airing-schedule-info.template.sh
This file was deleted.
Oops, something went wrong.
75 changes: 0 additions & 75 deletions
75
viu_media/assets/scripts/fzf/airing-schedule-preview.template.sh
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| import sys | ||
| from _ansi_utils import ( | ||
| print_rule, | ||
| print_table_row, | ||
| strip_markdown, | ||
| wrap_text, | ||
| get_terminal_width, | ||
| ) | ||
|
|
||
| HEADER_COLOR = sys.argv[1] | ||
| SEPARATOR_COLOR = sys.argv[2] | ||
|
|
||
| # Get terminal dimensions | ||
| term_width = get_terminal_width() | ||
|
|
||
| # Print title centered | ||
| print("{ANIME_TITLE}".center(term_width)) | ||
|
|
||
| rows = [ | ||
| ("Total Episodes", "{TOTAL_EPISODES}"), | ||
| ] | ||
|
|
||
| print_rule(SEPARATOR_COLOR) | ||
| for key, value in rows: | ||
| print_table_row(key, value, HEADER_COLOR, 15, term_width - 20) | ||
|
|
||
| rows = [ | ||
| ("Upcoming Episodes", "{UPCOMING_EPISODES}"), | ||
| ] | ||
|
|
||
| print_rule(SEPARATOR_COLOR) | ||
| for key, value in rows: | ||
| print_table_row(key, value, HEADER_COLOR, 15, term_width - 20) | ||
|
|
||
| print_rule(SEPARATOR_COLOR) | ||
| print(wrap_text(strip_markdown("""{SCHEDULE_TABLE}"""), term_width)) |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The type hint
tuple[int, int, int]uses Python 3.9+ syntax for generic types. For compatibility with Python 3.8 and earlier, this should beTuple[int, int, int]from thetypingmodule.Similarly, line 188 uses
int | None(Python 3.10+ syntax) instead ofOptional[int].Since these are standalone scripts that may run in various environments, consider using the more compatible
typingmodule syntax or document the minimum Python version requirement (3.10+) for these scripts.