feat: Bump version to 0.3.1, update filename extraction logic, and enhance rename confirmation screen

This commit is contained in:
sHa
2025-12-28 07:14:39 +00:00
parent 229478ce64
commit fe4c0a4a58
8 changed files with 86 additions and 21 deletions

BIN
dist/renamer-0.3.1-py3-none-any.whl vendored Normal file

Binary file not shown.

View File

@@ -1,6 +1,6 @@
[project] [project]
name = "renamer" name = "renamer"
version = "0.2.12" version = "0.3.1"
description = "Terminal-based media file renamer and metadata viewer" description = "Terminal-based media file renamer and metadata viewer"
readme = "README.md" readme = "README.md"
requires-python = ">=3.11" requires-python = ">=3.11"

View File

@@ -185,8 +185,11 @@ class RenamerApp(App):
extractor = MediaExtractor(node.data) extractor = MediaExtractor(node.data)
proposed_formatter = ProposedNameFormatter(extractor) proposed_formatter = ProposedNameFormatter(extractor)
new_name = str(proposed_formatter) new_name = str(proposed_formatter)
logging.info(f"Proposed new name: {new_name!r} for file: {node.data}")
if new_name and new_name != node.data.name: if new_name and new_name != node.data.name:
self.push_screen(RenameConfirmScreen(node.data, new_name)) self.push_screen(RenameConfirmScreen(node.data, new_name))
else:
self.notify("Proposed name is the same as current name; no rename needed.", severity="information", timeout=3)
async def action_expand(self): async def action_expand(self):
tree = self.query_one("#file_tree", Tree) tree = self.query_one("#file_tree", Tree)

View File

@@ -355,10 +355,16 @@ class FilenameExtractor:
if not langs: if not langs:
return '' return ''
# Count occurrences and format like mediainfo: "2ukr,eng" # Count occurrences while preserving order of first appearance
lang_counts = Counter(langs) lang_counts = {}
for lang in langs:
if lang not in lang_counts:
lang_counts[lang] = 0
lang_counts[lang] += 1
# Format like mediainfo: "2ukr,eng" preserving order
audio_langs = [f"{count}{lang}" if count > 1 else lang for lang, count in lang_counts.items()] audio_langs = [f"{count}{lang}" if count > 1 else lang for lang, count in lang_counts.items()]
return ','.join(sorted(audio_langs)) return ','.join(audio_langs)
def extract_audio_tracks(self) -> list[dict]: def extract_audio_tracks(self) -> list[dict]:
"""Extract audio track data from filename (simplified version with only language)""" """Extract audio track data from filename (simplified version with only language)"""

View File

@@ -17,7 +17,7 @@ class ProposedNameFormatter:
self.__frame_class = extractor.get("frame_class") or None self.__frame_class = extractor.get("frame_class") or None
self.__hdr = f",{extractor.get('hdr')}" if extractor.get("hdr") else "" self.__hdr = f",{extractor.get('hdr')}" if extractor.get("hdr") else ""
self.__audio_langs = extractor.get("audio_langs") or None self.__audio_langs = extractor.get("audio_langs") or None
self.__special_info = f" [{SpecialInfoFormatter.format_special_info(extractor.get('special_info'))}]" if extractor.get("special_info") else "" self.__special_info = f" \[{SpecialInfoFormatter.format_special_info(extractor.get('special_info'))}]" if extractor.get("special_info") else ""
self.__db_info = f" [{SpecialInfoFormatter.format_database_info(extractor.get('movie_db'))}]" if extractor.get("movie_db") else "" self.__db_info = f" [{SpecialInfoFormatter.format_database_info(extractor.get('movie_db'))}]" if extractor.get("movie_db") else ""
self.__extension = extractor.get("extension") or "ext" self.__extension = extractor.get("extension") or "ext"

View File

@@ -1,7 +1,7 @@
from textual.screen import Screen from textual.screen import Screen
from textual.widgets import Input, Button, Static from textual.widgets import Input, Button, Static
from textual.containers import Vertical, Horizontal, Center, Container from textual.containers import Vertical, Horizontal, Center, Container
from rich.markup import escape from textual.markup import escape
from pathlib import Path from pathlib import Path
@@ -97,18 +97,30 @@ class RenameConfirmScreen(Screen):
#confirm_content { #confirm_content {
text-align: center; text-align: center;
} }
Button { # Button {
background: $surface; # background: $surface;
border: solid $surface; # border: solid $surface;
} # }
Button:focus { Button:focus {
background: $primary; background: $primary;
color: $text-primary; # color: $text-primary;
border: solid $primary; # border: solid $primary;
} }
#buttons { #buttons {
align: center middle; align: center middle;
} }
#new_name_input {
width: 100%;
margin: 1 0;
}
#new_name_display {
text-align: center;
margin-bottom: 1;
}
#warning_content {
text-align: center;
margin-bottom: 0;
}
""" """
def __init__(self, old_path: Path, new_name: str): def __init__(self, old_path: Path, new_name: str):
@@ -123,17 +135,25 @@ class RenameConfirmScreen(Screen):
confirm_text = f""" confirm_text = f"""
{TextFormatter.bold(TextFormatter.red("RENAME CONFIRMATION"))} {TextFormatter.bold(TextFormatter.red("RENAME CONFIRMATION"))}
RAW name: {escape(self.old_path.name)}
Current name: {TextFormatter.cyan(escape(self.old_path.name))} Current name: {TextFormatter.cyan(escape(self.old_path.name))}
New name: {TextFormatter.green(escape(self.new_name))} Proposed name: {TextFormatter.green(escape(self.new_name))}
{TextFormatter.yellow("This action cannot be undone!")} {TextFormatter.yellow("Edit the new name below:")}
""".strip()
warning_text = f"""
{TextFormatter.bold(TextFormatter.red("This action cannot be undone!"))}
Do you want to proceed with renaming? Do you want to proceed with renaming?
""".strip() """.strip()
with Center(): with Center():
with Vertical(): with Vertical():
yield Static(confirm_text, id="confirm_content", markup=True) yield Static(confirm_text, id="confirm_content", markup=True)
yield Input(value=self.new_name, id="new_name_input", placeholder="New file name")
yield Static(f"{TextFormatter.green(escape(self.new_name))}", id="new_name_display", markup=True)
yield Static(warning_text, id="warning_content", markup=True)
with Horizontal(id="buttons"): with Horizontal(id="buttons"):
yield Button("Rename (y)", id="rename") yield Button("Rename (y)", id="rename")
yield Button("Cancel (n)", id="cancel") yield Button("Cancel (n)", id="cancel")
@@ -141,6 +161,15 @@ Do you want to proceed with renaming?
def on_mount(self): def on_mount(self):
self.set_focus(self.query_one("#rename")) self.set_focus(self.query_one("#rename"))
def on_input_changed(self, event):
if event.input.id == "new_name_input":
self.new_name = event.input.value
self.new_path = self.old_path.parent / self.new_name
# Update the display
from .formatters.text_formatter import TextFormatter
display = self.query_one("#new_name_display", Static)
display.update(f"{TextFormatter.green(escape(self.new_name))}")
def on_button_pressed(self, event): def on_button_pressed(self, event):
if event.button.id == "rename": if event.button.id == "rename":
try: try:
@@ -156,10 +185,37 @@ Do you want to proceed with renaming?
self.app.pop_screen() self.app.pop_screen()
def on_key(self, event): def on_key(self, event):
current = self.focused
if current and hasattr(current, 'id'):
if current.id == "new_name_input":
# When input is focused, let left/right move cursor, use up/down to change focus
if event.key == "up":
self.set_focus(self.query_one("#cancel"))
elif event.key == "down":
self.set_focus(self.query_one("#rename"))
elif current.id in ("rename", "cancel"):
if event.key == "left": if event.key == "left":
if current.id == "rename":
self.set_focus(self.query_one("#new_name_input"))
elif current.id == "cancel":
self.set_focus(self.query_one("#rename")) self.set_focus(self.query_one("#rename"))
elif event.key == "right": elif event.key == "right":
if current.id == "new_name_input":
self.set_focus(self.query_one("#rename"))
elif current.id == "rename":
self.set_focus(self.query_one("#cancel")) self.set_focus(self.query_one("#cancel"))
elif event.key == "up":
if current.id == "rename":
self.set_focus(self.query_one("#new_name_input"))
elif current.id == "cancel":
self.set_focus(self.query_one("#rename"))
elif event.key == "down":
if current.id == "new_name_input":
self.set_focus(self.query_one("#rename"))
elif current.id == "rename":
self.set_focus(self.query_one("#cancel"))
elif current.id == "cancel":
self.set_focus(self.query_one("#new_name_input"))
elif event.key == "y": elif event.key == "y":
# Trigger rename # Trigger rename
try: try:

2
uv.lock generated
View File

@@ -164,7 +164,7 @@ wheels = [
[[package]] [[package]]
name = "renamer" name = "renamer"
version = "0.2.12" version = "0.3.1"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "langcodes" }, { name = "langcodes" },