feat: Add genre extraction to media properties and icons to tree

This commit is contained in:
sHa
2026-01-03 15:34:27 +00:00
parent ef1e1e06ca
commit 0ec1fbe4db
7 changed files with 70 additions and 8 deletions

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

Binary file not shown.

View File

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

View File

@@ -160,6 +160,31 @@ class RenamerApp(App):
self.tree_expanded = False # Sub-levels are collapsed
self.set_focus(tree)
def _get_file_icon(self, file_path: Path) -> str:
"""Get icon for file based on extension.
Args:
file_path: Path to the file
Returns:
Icon character for the file type
"""
ext = file_path.suffix.lower().lstrip('.')
# File type icons
icons = {
'mkv': '📹', # Video camera for MKV
'mk3d': '🎬', # Clapper board for 3D
'avi': '🎞️', # Film frames for AVI
'mp4': '📹', # Video camera
'mov': '📹', # Video camera
'wmv': '📹', # Video camera
'webm': '📹', # Video camera
'm4v': '📹', # Video camera
}
return icons.get(ext, '📄') # Default to document icon
def build_tree(self, path: Path, node):
try:
for item in sorted(path.iterdir()):
@@ -167,13 +192,18 @@ class RenamerApp(App):
if item.is_dir():
if item.name.startswith(".") or item.name == "lost+found":
continue
subnode = node.add(escape(item.name), data=item)
# Add folder icon before directory name
label = f"📁 {escape(item.name)}"
subnode = node.add(label, data=item)
self.build_tree(item, subnode)
elif item.is_file() and item.suffix.lower() in {
f".{ext}" for ext in MEDIA_TYPES
}:
logging.info(f"Adding file to tree: {item.name!r} (full path: {item})")
node.add(escape(item.name), data=item)
# Add file type icon before filename
icon = self._get_file_icon(item)
label = f"{icon} {escape(item.name)}"
node.add(label, data=item)
except PermissionError:
pass
except PermissionError:
@@ -452,7 +482,9 @@ By Category:"""
node = find_node(tree.root)
if node:
logging.info(f"Found node for {old_path}, updating to {new_path.name}")
node.label = escape(new_path.name)
# Update label with icon
icon = self._get_file_icon(new_path)
node.label = f"{icon} {escape(new_path.name)}"
node.data = new_path
logging.info(f"After update: node.data = {node.data}, node.label = {node.label}")
# Ensure cursor stays on the renamed file
@@ -513,6 +545,10 @@ By Category:"""
if parent_node:
logging.info(f"Found parent node for {parent_dir}, adding file {file_path.name}")
# Get icon for the file
icon = self._get_file_icon(file_path)
label = f"{icon} {escape(file_path.name)}"
# Add the new file node in alphabetically sorted position
new_node = None
inserted = False
@@ -522,14 +558,14 @@ By Category:"""
# Compare filenames for sorting
if child.data.name > file_path.name:
# Insert before this child
new_node = parent_node.add(escape(file_path.name), data=file_path, before=i)
new_node = parent_node.add(label, data=file_path, before=i)
inserted = True
logging.info(f"Inserted file before {child.data.name}")
break
# If not inserted, add at the end
if not inserted:
new_node = parent_node.add(escape(file_path.name), data=file_path)
new_node = parent_node.add(label, data=file_path)
logging.info(f"Added file at end of directory")
# Select the new node and show its details

View File

@@ -200,6 +200,12 @@ class MediaExtractor:
("Default", "extract_subtitle_tracks"),
],
},
"genres": {
"sources": [
("TMDB", "extract_genres"),
("Default", "extract_genres"),
],
},
}
def get(self, key: str, source: str | None = None):

View File

@@ -35,6 +35,7 @@ class MediaPanelView:
self._props.title("Media Info Summary"),
self._props.media_title,
self._props.media_year,
self._props.media_genres,
self._props.media_duration,
self._props.media_file_size,
self._props.media_file_extension,
@@ -70,6 +71,7 @@ class MediaPanelView:
self._props.tmdb_title,
self._props.tmdb_original_title,
self._props.tmdb_year,
self._props.tmdb_genres,
self._props.tmdb_database_info,
self._props.tmdb_url,
]

View File

@@ -123,6 +123,15 @@ class MediaPanelProperties:
"""Get TMDB year formatted with label."""
return self._extractor.get("year", "TMDB")
@property
@text_decorators.blue()
@conditional_decorators.wrap("Genres: ")
@text_decorators.yellow()
@conditional_decorators.default("<None>")
def tmdb_genres(self) -> str:
"""Get TMDB genres formatted with label."""
return self._extractor.get("genres", "TMDB")
@property
@text_decorators.blue()
@conditional_decorators.wrap("Database Info: ")
@@ -328,7 +337,7 @@ class MediaPanelProperties:
return self._extractor.get("movie_db", "Filename")
# ============================================================
# Joined Data Properties
# Media Data Properties
# ============================================================
@property
@@ -367,6 +376,15 @@ class MediaPanelProperties:
"""Get selected year formatted with label."""
return self._extractor.get("year")
@property
@text_decorators.blue()
@conditional_decorators.wrap("Genres: ")
@text_decorators.cyan()
@conditional_decorators.default("<None>")
def media_genres(self) -> str:
"""Get TMDB genres formatted with label."""
return self._extractor.get("genres")
@property
@text_decorators.blue()
@conditional_decorators.wrap("File size: ")

2
uv.lock generated
View File

@@ -462,7 +462,7 @@ wheels = [
[[package]]
name = "renamer"
version = "0.6.8"
version = "0.6.9"
source = { editable = "." }
dependencies = [
{ name = "langcodes" },