From 9877be17a39e79ae8991740b4aecd0f0ae87db5a Mon Sep 17 00:00:00 2001 From: sHa Date: Sat, 27 Dec 2025 04:02:32 +0000 Subject: [PATCH] feat: Bump version to 0.2.9, enhance frame class extraction and add build script --- ToDo.md | 9 +++---- dist/renamer-0.2.9-py3-none-any.whl | Bin 0 -> 33241 bytes pyproject.toml | 2 +- renamer/constants.py | 15 ++++++++++++ renamer/extractors/filename_extractor.py | 13 ++++++++--- renamer/extractors/mediainfo_extractor.py | 22 +++++++++++++++++- ...alto Wolf Quest (2002) [1080i,ukr,eng].mkv | 0 uv.lock | 2 +- 8 files changed, 53 insertions(+), 10 deletions(-) create mode 100644 dist/renamer-0.2.9-py3-none-any.whl create mode 100644 renamer/test/filenames/[02] Balto Wolf Quest (2002) [1080i,ukr,eng].mkv diff --git a/ToDo.md b/ToDo.md index 4efb45b..aff9a8a 100644 --- a/ToDo.md +++ b/ToDo.md @@ -23,7 +23,8 @@ TODO Steps: 20. ✅ Add loading indicators for metadata extraction 21. ✅ Add error handling for file operations and metadata extraction 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 +23. 🔄 Implement build script to exclude dev commands (bump-version, release) from distributed package +24. Implement metadata editing capabilities (future enhancement) +25. Add batch rename operations (future enhancement) +26. Add configuration file support (future enhancement) +27. Add plugin system for custom extractors/formatters (future enhancement) \ No newline at end of file diff --git a/dist/renamer-0.2.9-py3-none-any.whl b/dist/renamer-0.2.9-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..6efd1e58d96592a405c8bf600ed2599ef25d3325 GIT binary patch literal 33241 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 str: """Normalize Cyrillic characters to English equivalents for parsing""" @@ -157,10 +156,18 @@ class FilenameExtractor: def extract_frame_class(self) -> str | None: """Extract frame class from filename (480p, 720p, 1080p, 2160p, etc.)""" - # First check for specific numeric resolutions - match = re.search(r'(\d{3,4})[pi]', self.file_name, re.IGNORECASE) + # Normalize Cyrillic characters for resolution parsing + normalized_name = self._normalize_cyrillic(self.file_name) + + # First check for specific numeric resolutions with p/i + match = re.search(r'(\d{3,4})([pi])', normalized_name, re.IGNORECASE) if match: height = int(match.group(1)) + scan_type = match.group(2).lower() + frame_class = f"{height}{scan_type}" + if frame_class in FRAME_CLASSES: + return frame_class + # Fallback to height-based if not in constants return self._get_frame_class_from_height(height) # If no specific resolution found, check for quality indicators diff --git a/renamer/extractors/mediainfo_extractor.py b/renamer/extractors/mediainfo_extractor.py index 77c5819..4b2177d 100644 --- a/renamer/extractors/mediainfo_extractor.py +++ b/renamer/extractors/mediainfo_extractor.py @@ -59,7 +59,27 @@ class MediaInfoExtractor: return None height = getattr(self.video_tracks[0], 'height', None) if height: - return self._get_frame_class_from_height(height) + # Check if interlaced + interlaced = getattr(self.video_tracks[0], 'interlaced', None) + scan_type = 'i' if interlaced == 'Yes' else 'p' + + # First try exact match + frame_class = f"{height}{scan_type}" + if frame_class in FRAME_CLASSES: + return frame_class + + # Find closest height with same scan type + closest_height = None + min_diff = float('inf') + for fc, info in FRAME_CLASSES.items(): + if fc.endswith(scan_type): + diff = abs(height - info['nominal_height']) + if diff < min_diff: + min_diff = diff + closest_height = info['nominal_height'] + + if closest_height and min_diff <= 50: + return f"{closest_height}{scan_type}" return None def extract_resolution(self) -> tuple[int, int] | None: diff --git a/renamer/test/filenames/[02] Balto Wolf Quest (2002) [1080i,ukr,eng].mkv b/renamer/test/filenames/[02] Balto Wolf Quest (2002) [1080i,ukr,eng].mkv new file mode 100644 index 0000000..e69de29 diff --git a/uv.lock b/uv.lock index b79f558..c52c436 100644 --- a/uv.lock +++ b/uv.lock @@ -164,7 +164,7 @@ wheels = [ [[package]] name = "renamer" -version = "0.2.8" +version = "0.2.9" source = { editable = "." } dependencies = [ { name = "langcodes" },