From 95cbaee7fac2de0695e6fea84fc348282ef75e35 Mon Sep 17 00:00:00 2001 From: sHa Date: Sat, 27 Dec 2025 01:31:21 +0000 Subject: [PATCH] feat: Update version to 0.2.4, enhance logging capabilities, and improve special info formatting --- .gitignore | 2 +- README.md | 18 +++ ToDo.md | 9 +- dist/renamer-0.2.0-py3-none-any.whl | Bin 30004 -> 0 bytes ...any.whl => renamer-0.2.4-py3-none-any.whl} | Bin 30368 -> 31797 bytes pyproject.toml | 2 +- renamer/constants.py | 5 + renamer/formatters/formatter.py | 132 ++++++++++++++++++ renamer/formatters/media_formatter.py | 83 ++--------- renamer/formatters/special_info_formatter.py | 4 +- uv.lock | 2 +- 11 files changed, 179 insertions(+), 78 deletions(-) delete mode 100644 dist/renamer-0.2.0-py3-none-any.whl rename dist/{renamer-0.2.2-py3-none-any.whl => renamer-0.2.4-py3-none-any.whl} (53%) create mode 100644 renamer/formatters/formatter.py diff --git a/.gitignore b/.gitignore index 90aa8dc..f1ba00b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,6 @@ __pycache__/ build/ wheels/ *.egg-info - +*.log # Virtual environments .venv diff --git a/README.md b/README.md index eae8f5e..7b3d665 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,24 @@ renamer /path/to/media/directory 4. Press **y** to confirm or **n** to cancel 5. The file will be renamed and the tree updated automatically +## Debugging + +### Formatter Logging +The application includes detailed logging for formatter operations that can be enabled for debugging purposes. + +To enable formatter logging: +```bash +FORMATTER_LOG=1 renamer /path/to/directory +``` + +This will create a `formatter.log` file in the current directory containing: +- Formatter call sequences and ordering +- Input/output values for each formatter +- Caller information (file and line number) +- Any errors during formatting + +Useful for troubleshooting metadata display issues or formatter problems. + ## Architecture The application uses a modular architecture with separate extractors and formatters: diff --git a/ToDo.md b/ToDo.md index 12a611c..4efb45b 100644 --- a/ToDo.md +++ b/ToDo.md @@ -22,7 +22,8 @@ TODO Steps: 19. ✅ Optimize tree updates to avoid full reloads after renaming 20. ✅ Add loading indicators for metadata extraction 21. ✅ Add error handling for file operations and metadata extraction -22. Implement metadata editing capabilities (future enhancement) -23. Add batch rename operations (future enhancement) -24. Add configuration file support (future enhancement) -25. Add plugin system for custom extractors/formatters (future enhancement) \ No newline at end of file +22. 🔄 Implement blue highlighting for changed parts in proposed filename display (show differences between current and proposed names) +23. Implement metadata editing capabilities (future enhancement) +24. Add batch rename operations (future enhancement) +25. Add configuration file support (future enhancement) +26. Add plugin system for custom extractors/formatters (future enhancement) \ No newline at end of file diff --git a/dist/renamer-0.2.0-py3-none-any.whl b/dist/renamer-0.2.0-py3-none-any.whl deleted file mode 100644 index 6f576c2c0255686c78fafddea473760ee034d8bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30004 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 E4|+(Yr~m)} delta 8303 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 n|7z{ {repr(value)}") + except Exception as e: + logging.error(f"Error applying {formatter.__name__ if hasattr(formatter, '__name__') else str(formatter)}: {e}") + value = "Unknown" + + logging.info(f"Final value: {repr(value)}") + return value + + @staticmethod + def format_data_item(item: dict) -> str | None: + """Apply all formatting to a data item and return the formatted string""" + # Handle value formatting first (e.g., size formatting) + value = item.get("value") + if value is not None and value != "Not extracted": + value_formatters = item.get("value_formatters", []) + value = FormatterApplier.apply_formatters(value, value_formatters) + + # Handle label formatting + label = item.get("label", "") + if label: + label_formatters = item.get("label_formatters", []) + label = FormatterApplier.apply_formatters(label, label_formatters) + + # Create the display string + if value is not None: + display_string = f"{label}: {value}" + else: + display_string = label + + # Handle display formatting (e.g., color) + display_formatters = item.get("display_formatters", []) + display_string = FormatterApplier.apply_formatters(display_string, display_formatters) + + return display_string + + @staticmethod + def format_data_items(data: list[dict]) -> list: + """Apply formatting to a list of data items""" + return [FormatterApplier.format_data_item(item) for item in data] \ No newline at end of file diff --git a/renamer/formatters/media_formatter.py b/renamer/formatters/media_formatter.py index 49e90c3..4c0fd28 100644 --- a/renamer/formatters/media_formatter.py +++ b/renamer/formatters/media_formatter.py @@ -8,6 +8,7 @@ from .track_formatter import TrackFormatter from .resolution_formatter import ResolutionFormatter from .duration_formatter import DurationFormatter from .special_info_formatter import SpecialInfoFormatter +from .formatter import FormatterApplier class MediaFormatter: @@ -16,56 +17,6 @@ class MediaFormatter: def __init__(self, extractor): self.extractor = extractor - def _format_data_item(self, item: dict) -> str: - """Apply all formatting to a data item and return the formatted string""" - # Define text formatters that should be applied before markup - text_formatters_set = { - TextFormatter.uppercase, - TextFormatter.lowercase, - TextFormatter.camelcase, - } - - # Handle value formatting first (e.g., size formatting) - value = item.get("value") - if value is not None and not isinstance(value, str): - value_formatters = item.get("value_formatters", []) - if not isinstance(value_formatters, list): - value_formatters = [value_formatters] if value_formatters else [] - for formatter in value_formatters: - value = formatter(value) - - # Handle label formatting - label = item.get("label", "") - if label: - label_formatters = item.get("label_formatters", []) - if not isinstance(label_formatters, list): - label_formatters = [label_formatters] if label_formatters else [] - # Separate text and markup formatters, apply text first - text_fs = [f for f in label_formatters if f in text_formatters_set] - markup_fs = [f for f in label_formatters if f not in text_formatters_set] - ordered_formatters = text_fs + markup_fs - for formatter in ordered_formatters: - label = formatter(label) - - # Create the display string - if value is not None: - display_string = f"{label}: {value}" - else: - display_string = label - - # Handle display formatting (e.g., color) - display_formatters = item.get("display_formatters", []) - if not isinstance(display_formatters, list): - display_formatters = [display_formatters] if display_formatters else [] - # Separate text and markup formatters, apply text first - text_fs = [f for f in display_formatters if f in text_formatters_set] - markup_fs = [f for f in display_formatters if f not in text_formatters_set] - ordered_formatters = text_fs + markup_fs - for formatter in ordered_formatters: - display_string = formatter(display_string) - - return display_string - def file_info_panel(self) -> str: """Return formatted file info panel string""" sections = [ @@ -123,7 +74,7 @@ class MediaFormatter: "display_formatters": [TextFormatter.green], }, ] - return [self._format_data_item(item) for item in data] + return FormatterApplier.format_data_items(data) def tracks_info(self) -> list[str]: """Return formatted tracks information""" @@ -174,7 +125,7 @@ class MediaFormatter: } ) - return [self._format_data_item(item) for item in data] + return FormatterApplier.format_data_items(data) def metadata_extracted_data(self) -> list[str]: """Format metadata extraction data for the metadata panel""" @@ -204,7 +155,7 @@ class MediaFormatter: }, ] - return [self._format_data_item(item) for item in data] + return FormatterApplier.format_data_items(data) def mediainfo_extracted_data(self) -> list[str]: """Format media info extraction data for the mediainfo panel""" @@ -256,7 +207,7 @@ class MediaFormatter: "display_formatters": [TextFormatter.grey], }, ] - return [self._format_data_item(item) for item in data] + return FormatterApplier.format_data_items(data) def filename_extracted_data(self) -> list[str]: """Return formatted filename extracted data""" @@ -328,7 +279,7 @@ class MediaFormatter: }, ] - return [self._format_data_item(item) for item in data] + return FormatterApplier.format_data_items(data) def selected_data(self) -> list[str]: """Return formatted selected data string""" @@ -337,6 +288,12 @@ class MediaFormatter: "label": "Selected Data", "label_formatters": [TextFormatter.bold, TextFormatter.uppercase], }, + { + "label": "Title", + "label_formatters": [TextFormatter.bold, TextFormatter.yellow], + "value": self.extractor.get("title") or "", + "value_formatters": [TextFormatter.blue], + }, { "label": "Special info", "label_formatters": [TextFormatter.bold], @@ -348,18 +305,4 @@ class MediaFormatter: "display_formatters": [TextFormatter.yellow], }, ] - return [self._format_data_item(item) for item in data] - - def _format_extra_metadata(self, metadata: dict) -> str: - """Format extra metadata like duration, title, artist""" - data = {} - if metadata.get("duration"): - data["Duration"] = f"{metadata['duration']:.1f} seconds" - if metadata.get("title"): - data["Title"] = metadata["title"] - if metadata.get("artist"): - data["Artist"] = metadata["artist"] - - return "\n".join( - TextFormatter.cyan(f"{key}: {value}") for key, value in data.items() - ) + return FormatterApplier.format_data_items(data) diff --git a/renamer/formatters/special_info_formatter.py b/renamer/formatters/special_info_formatter.py index 63dff59..617e4e1 100644 --- a/renamer/formatters/special_info_formatter.py +++ b/renamer/formatters/special_info_formatter.py @@ -5,5 +5,7 @@ class SpecialInfoFormatter: def format_special_info(special_info): """Convert special info list to comma-separated string""" if isinstance(special_info, list): - return ", ".join(special_info) + # Filter out None values and ensure all items are strings + filtered = [str(item) for item in special_info if item is not None] + return ", ".join(filtered) return special_info or "" \ No newline at end of file diff --git a/uv.lock b/uv.lock index 4752a2c..fd88f24 100644 --- a/uv.lock +++ b/uv.lock @@ -164,7 +164,7 @@ wheels = [ [[package]] name = "renamer" -version = "0.2.3" +version = "0.2.4" source = { editable = "." } dependencies = [ { name = "langcodes" },