feat: Enhance renamer app with help screen, rename confirmation, and special info formatting

This commit is contained in:
sha
2025-12-26 23:11:16 +00:00
parent 691d1e7b2d
commit a6507dec31
5 changed files with 259 additions and 17 deletions
+71 -6
View File
@@ -4,16 +4,18 @@ from textual.containers import Horizontal, Container, ScrollableContainer, Verti
from pathlib import Path
import threading
import time
import concurrent.futures
from .constants import MEDIA_TYPES
from .screens import OpenScreen
from .screens import OpenScreen, HelpScreen, RenameConfirmScreen
from .extractor import MediaExtractor
from .formatters.media_formatter import MediaFormatter
from .formatters.proposed_name_formatter import ProposedNameFormatter
from .formatters.text_formatter import TextFormatter
VERSION = "0.2.0"
class RenamerApp(App):
CSS = """
#left {
@@ -30,12 +32,16 @@ class RenamerApp(App):
("q", "quit", "Quit"),
("o", "open", "Open directory"),
("s", "scan", "Scan"),
("r", "refresh", "Refresh"),
("f", "refresh", "Refresh"),
("r", "rename", "Rename"),
("p", "expand", "Toggle Tree"),
("h", "help", "Help"),
]
def __init__(self, scan_dir):
super().__init__()
self.scan_dir = Path(scan_dir) if scan_dir else None
self.tree_expanded = False
def compose(self) -> ComposeResult:
with Horizontal():
@@ -57,14 +63,15 @@ class RenamerApp(App):
self.scan_files()
def scan_files(self):
if not self.scan_dir.exists() or not self.scan_dir.is_dir():
if not self.scan_dir or not self.scan_dir.exists() or not self.scan_dir.is_dir():
details = self.query_one("#details", Static)
details.update("Error: Directory does not exist or is not a directory")
return
tree = self.query_one("#file_tree", Tree)
tree.clear()
self.build_tree(self.scan_dir, tree.root)
tree.root.expand()
tree.root.expand() # Expand root level
self.tree_expanded = False # Sub-levels are collapsed
self.set_focus(tree)
def build_tree(self, path: Path, node):
@@ -122,7 +129,7 @@ class RenamerApp(App):
self.call_later(
self._update_details,
MediaFormatter(extractor).file_info_panel(),
ProposedNameFormatter(extractor).rename_line_formatted(),
ProposedNameFormatter(extractor).rename_line_formatted(file_path),
)
except Exception as e:
self.call_later(
@@ -158,6 +165,64 @@ class RenamerApp(App):
target=self._extract_and_show_details, args=(node.data,)
).start()
async def action_help(self):
self.push_screen(HelpScreen())
async def action_rename(self):
tree = self.query_one("#file_tree", Tree)
node = tree.cursor_node
if node and node.data and isinstance(node.data, Path) and node.data.is_file():
# Get the proposed name from the extractor
extractor = MediaExtractor(node.data)
proposed_formatter = ProposedNameFormatter(extractor)
new_name = str(proposed_formatter)
if new_name and new_name != node.data.name:
self.push_screen(RenameConfirmScreen(node.data, new_name))
async def action_expand(self):
tree = self.query_one("#file_tree", Tree)
if self.tree_expanded:
# Collapse all sub-levels, keep root expanded
def collapse_sub(node):
if node != tree.root:
node.collapse()
for child in node.children:
collapse_sub(child)
collapse_sub(tree.root)
self.tree_expanded = False
else:
# Expand all
def expand_all(node):
node.expand()
for child in node.children:
expand_all(child)
expand_all(tree.root)
self.tree_expanded = True
def update_renamed_file(self, old_path: Path, new_path: Path):
"""Update the tree node for a renamed file."""
tree = self.query_one("#file_tree", Tree)
def find_node(node):
if node.data == old_path:
return node
for child in node.children:
found = find_node(child)
if found:
return found
return None
node = find_node(tree.root)
if node:
node.label = new_path.name
node.data = new_path
# If this node is currently selected, refresh the details
if tree.cursor_node == node:
self._start_loading_animation()
threading.Thread(
target=self._extract_and_show_details, args=(new_path,)
).start()
def on_key(self, event):
if event.key == "right":
tree = self.query_one("#file_tree", Tree)