feat: Bump version to 0.2.9, enhance frame class extraction and add build script
This commit is contained in:
9
ToDo.md
9
ToDo.md
@@ -23,7 +23,8 @@ TODO Steps:
|
|||||||
20. ✅ Add loading indicators for metadata extraction
|
20. ✅ Add loading indicators for metadata extraction
|
||||||
21. ✅ Add error handling for file operations and 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)
|
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)
|
23. 🔄 Implement build script to exclude dev commands (bump-version, release) from distributed package
|
||||||
24. Add batch rename operations (future enhancement)
|
24. Implement metadata editing capabilities (future enhancement)
|
||||||
25. Add configuration file support (future enhancement)
|
25. Add batch rename operations (future enhancement)
|
||||||
26. Add plugin system for custom extractors/formatters (future enhancement)
|
26. Add configuration file support (future enhancement)
|
||||||
|
27. Add plugin system for custom extractors/formatters (future enhancement)
|
||||||
BIN
dist/renamer-0.2.9-py3-none-any.whl
vendored
Normal file
BIN
dist/renamer-0.2.9-py3-none-any.whl
vendored
Normal file
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "renamer"
|
name = "renamer"
|
||||||
version = "0.2.8"
|
version = "0.2.9"
|
||||||
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"
|
||||||
|
|||||||
@@ -51,11 +51,21 @@ FRAME_CLASSES = {
|
|||||||
"typical_widths": [640, 704, 720],
|
"typical_widths": [640, 704, 720],
|
||||||
"description": "Standard Definition (SD) - DVD quality",
|
"description": "Standard Definition (SD) - DVD quality",
|
||||||
},
|
},
|
||||||
|
"480i": {
|
||||||
|
"nominal_height": 480,
|
||||||
|
"typical_widths": [640, 704, 720],
|
||||||
|
"description": "Standard Definition (SD) interlaced - NTSC quality",
|
||||||
|
},
|
||||||
"576p": {
|
"576p": {
|
||||||
"nominal_height": 576,
|
"nominal_height": 576,
|
||||||
"typical_widths": [720, 768],
|
"typical_widths": [720, 768],
|
||||||
"description": "PAL Standard Definition (SD) - European DVD quality",
|
"description": "PAL Standard Definition (SD) - European DVD quality",
|
||||||
},
|
},
|
||||||
|
"576i": {
|
||||||
|
"nominal_height": 576,
|
||||||
|
"typical_widths": [720, 768],
|
||||||
|
"description": "PAL Standard Definition (SD) interlaced - European quality",
|
||||||
|
},
|
||||||
"720p": {
|
"720p": {
|
||||||
"nominal_height": 720,
|
"nominal_height": 720,
|
||||||
"typical_widths": [1280],
|
"typical_widths": [1280],
|
||||||
@@ -66,6 +76,11 @@ FRAME_CLASSES = {
|
|||||||
"typical_widths": [1920],
|
"typical_widths": [1920],
|
||||||
"description": "Full High Definition (FHD) - 1080p HD",
|
"description": "Full High Definition (FHD) - 1080p HD",
|
||||||
},
|
},
|
||||||
|
"1080i": {
|
||||||
|
"nominal_height": 1080,
|
||||||
|
"typical_widths": [1920],
|
||||||
|
"description": "Full High Definition (FHD) interlaced - 1080i HD",
|
||||||
|
},
|
||||||
"1440p": {
|
"1440p": {
|
||||||
"nominal_height": 1440,
|
"nominal_height": 1440,
|
||||||
"typical_widths": [2560],
|
"typical_widths": [2560],
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ class FilenameExtractor:
|
|||||||
def __init__(self, file_path: Path):
|
def __init__(self, file_path: Path):
|
||||||
self.file_path = file_path
|
self.file_path = file_path
|
||||||
self.file_name = file_path.name
|
self.file_name = file_path.name
|
||||||
self.file_name = self._normalize_cyrillic(self.file_name)
|
|
||||||
|
|
||||||
def _normalize_cyrillic(self, text: str) -> str:
|
def _normalize_cyrillic(self, text: str) -> str:
|
||||||
"""Normalize Cyrillic characters to English equivalents for parsing"""
|
"""Normalize Cyrillic characters to English equivalents for parsing"""
|
||||||
@@ -157,10 +156,18 @@ class FilenameExtractor:
|
|||||||
|
|
||||||
def extract_frame_class(self) -> str | None:
|
def extract_frame_class(self) -> str | None:
|
||||||
"""Extract frame class from filename (480p, 720p, 1080p, 2160p, etc.)"""
|
"""Extract frame class from filename (480p, 720p, 1080p, 2160p, etc.)"""
|
||||||
# First check for specific numeric resolutions
|
# Normalize Cyrillic characters for resolution parsing
|
||||||
match = re.search(r'(\d{3,4})[pi]', self.file_name, re.IGNORECASE)
|
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:
|
if match:
|
||||||
height = int(match.group(1))
|
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)
|
return self._get_frame_class_from_height(height)
|
||||||
|
|
||||||
# If no specific resolution found, check for quality indicators
|
# If no specific resolution found, check for quality indicators
|
||||||
|
|||||||
@@ -59,7 +59,27 @@ class MediaInfoExtractor:
|
|||||||
return None
|
return None
|
||||||
height = getattr(self.video_tracks[0], 'height', None)
|
height = getattr(self.video_tracks[0], 'height', None)
|
||||||
if height:
|
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
|
return None
|
||||||
|
|
||||||
def extract_resolution(self) -> tuple[int, int] | None:
|
def extract_resolution(self) -> tuple[int, int] | None:
|
||||||
|
|||||||
Reference in New Issue
Block a user