feat: Add order extraction from filename and integrate into proposed name formatting
This commit is contained in:
@@ -30,6 +30,7 @@ class RenamerApp(App):
|
||||
("q", "quit", "Quit"),
|
||||
("o", "open", "Open directory"),
|
||||
("s", "scan", "Scan"),
|
||||
("r", "refresh", "Refresh"),
|
||||
]
|
||||
|
||||
def __init__(self, scan_dir):
|
||||
@@ -148,6 +149,15 @@ class RenamerApp(App):
|
||||
if self.scan_dir:
|
||||
self.scan_files()
|
||||
|
||||
async def action_refresh(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():
|
||||
self._start_loading_animation()
|
||||
threading.Thread(
|
||||
target=self._extract_and_show_details, args=(node.data,)
|
||||
).start()
|
||||
|
||||
def on_key(self, event):
|
||||
if event.key == "right":
|
||||
tree = self.query_one("#file_tree", Tree)
|
||||
|
||||
@@ -46,6 +46,12 @@ class MediaExtractor:
|
||||
("Default", "extract_source"),
|
||||
],
|
||||
},
|
||||
"order": {
|
||||
"sources": [
|
||||
("Filename", "extract_order"),
|
||||
("Default", "extract_order"),
|
||||
],
|
||||
},
|
||||
"frame_class": {
|
||||
"sources": [
|
||||
("MediaInfo", "extract_frame_class"),
|
||||
|
||||
@@ -10,6 +10,9 @@ class DefaultExtractor:
|
||||
def extract_source(self):
|
||||
return None
|
||||
|
||||
def extract_order(self):
|
||||
return None
|
||||
|
||||
def extract_resolution(self):
|
||||
return None
|
||||
|
||||
|
||||
@@ -73,6 +73,13 @@ class FilenameExtractor:
|
||||
# Remove bracketed prefixes like [01.1], [1], etc.
|
||||
title = re.sub(r'^\s*\[[^\]]+\]\s*', '', title)
|
||||
|
||||
# Remove order number prefixes like 01., 1., 1.1 followed by space/underscore
|
||||
title = re.sub(r'^\s*(\d+(?:\.\d+)?)\.(?=\s|_|$)', '', title)
|
||||
title = re.sub(r'^\s*(\d+(?:\.\d+)?)(?=\s|_)', '', title)
|
||||
|
||||
# Clean up any remaining leading separators
|
||||
title = title.lstrip('_ \t')
|
||||
|
||||
# Clean up title: remove leading/trailing brackets and dots
|
||||
title = title.strip('[](). ')
|
||||
|
||||
@@ -114,6 +121,28 @@ class FilenameExtractor:
|
||||
return src
|
||||
return None
|
||||
|
||||
def extract_order(self) -> str | None:
|
||||
"""Extract collection order number from filename (at the beginning)"""
|
||||
# Look for order patterns at the start of filename
|
||||
# Patterns: [01], [01.1], 01., 1., 1.1 followed by space or underscore
|
||||
|
||||
# Check for bracketed patterns: [01], [01.1], etc.
|
||||
bracket_match = re.match(r'^\[(\d+(?:\.\d+)?)\]', self.file_name)
|
||||
if bracket_match:
|
||||
return bracket_match.group(1)
|
||||
|
||||
# Check for dot patterns: 01., 1., 1.1 followed by space, underscore, or end of string
|
||||
dot_match = re.match(r'^(\d+(?:\.\d+)?)\.(?=\s|_|$)', self.file_name)
|
||||
if dot_match:
|
||||
return dot_match.group(1)
|
||||
|
||||
# Check for number followed by space or underscore (like "1.1 " at start)
|
||||
space_match = re.match(r'^(\d+(?:\.\d+)?)(?=\s|_)', self.file_name)
|
||||
if space_match:
|
||||
return space_match.group(1)
|
||||
|
||||
return None
|
||||
|
||||
def extract_frame_class(self) -> str | None:
|
||||
"""Extract frame class from filename (480p, 720p, 1080p, 2160p, etc.)"""
|
||||
# First check for specific numeric resolutions
|
||||
|
||||
@@ -263,6 +263,12 @@ class MediaFormatter:
|
||||
"label": "Filename Extracted Data",
|
||||
"label_formatters": [TextFormatter.bold, TextFormatter.uppercase],
|
||||
},
|
||||
{
|
||||
"label": "Order",
|
||||
"label_formatters": [TextFormatter.bold],
|
||||
"value": self.extractor.get("order", "Filename") or "Not extracted",
|
||||
"display_formatters": [TextFormatter.grey],
|
||||
},
|
||||
{
|
||||
"label": "Movie title",
|
||||
"label_formatters": [TextFormatter.bold],
|
||||
|
||||
@@ -8,6 +8,7 @@ class ProposedNameFormatter:
|
||||
def __init__(self, extractor):
|
||||
"""Initialize with media extractor data"""
|
||||
|
||||
self.__order = f"[{extractor.get('order')}] " if extractor.get("order") else ""
|
||||
self.__title = extractor.get("title") or "Unknown Title"
|
||||
self.__year = DateFormatter.format_year(extractor.get("year"))
|
||||
self.__source = f" {extractor.get('source')}" if extractor.get("source") else ""
|
||||
@@ -21,7 +22,7 @@ class ProposedNameFormatter:
|
||||
return self.rename_line()
|
||||
|
||||
def rename_line(self) -> str:
|
||||
return f"{self.__title} {self.__year}{self.__source} [{self.__frame_class}{self.__hdr},{self.__audio_langs}].{self.__extension}"
|
||||
return f"{self.__order}{self.__title} {self.__year}{self.__source} [{self.__frame_class}{self.__hdr},{self.__audio_langs}].{self.__extension}"
|
||||
|
||||
def rename_line_formatted(self) -> str:
|
||||
"""Format the proposed name for display with color"""
|
||||
|
||||
Reference in New Issue
Block a user