From eedc32bf31caab6127f8a48f03c689fc3587d914 Mon Sep 17 00:00:00 2001 From: sHa Date: Sun, 28 Dec 2025 17:06:29 +0000 Subject: [PATCH] feat: Update version to 0.4.7 and add new extraction methods for anamorphic, extension, and 3D layout --- dist/renamer-0.4.7-py3-none-any.whl | Bin 0 -> 39832 bytes pyproject.toml | 2 +- renamer/extractors/default_extractor.py | 8 ++- renamer/extractors/extractor.py | 13 +++++ renamer/extractors/mediainfo_extractor.py | 60 +++++++++++++++++++++- renamer/formatters/media_formatter.py | 19 +++++++ renamer/test/test_mediainfo_extractor.py | 20 +++++++- uv.lock | 2 +- 8 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 dist/renamer-0.4.7-py3-none-any.whl diff --git a/dist/renamer-0.4.7-py3-none-any.whl b/dist/renamer-0.4.7-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..026e6ef55f8413f1e643dd2136afa56b78ba00e3 GIT binary patch literal 39832 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 VGjy0Ica#yQN`emX#3=H+zX2Hge4qdT literal 0 HcmV?d00001 diff --git a/pyproject.toml b/pyproject.toml index 1e1ccdd..8a94e18 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "renamer" -version = "0.4.6" +version = "0.4.7" description = "Terminal-based media file renamer and metadata viewer" readme = "README.md" requires-python = ">=3.11" diff --git a/renamer/extractors/default_extractor.py b/renamer/extractors/default_extractor.py index e9c7223..9b1834f 100644 --- a/renamer/extractors/default_extractor.py +++ b/renamer/extractors/default_extractor.py @@ -23,7 +23,7 @@ class DefaultExtractor: return None def extract_special_info(self): - return [] + return None def extract_audio_langs(self): return None @@ -55,6 +55,12 @@ class DefaultExtractor: def extract_subtitle_tracks(self): return [] + def extract_anamorphic(self): + return None + + def extract_extension(self): + return None + def extract_tmdb_url(self): return None diff --git a/renamer/extractors/extractor.py b/renamer/extractors/extractor.py index 2dfd438..05d10d7 100644 --- a/renamer/extractors/extractor.py +++ b/renamer/extractors/extractor.py @@ -76,6 +76,18 @@ class MediaExtractor: ("Default", "extract_hdr"), ], }, + "anamorphic": { + "sources": [ + ("MediaInfo", "extract_anamorphic"), + ("Default", "extract_anamorphic"), + ], + }, + "3d_layout": { + "sources": [ + ("MediaInfo", "extract_3d_layout"), + ("Default", "extract_3d_layout"), + ], + }, "movie_db": { "sources": [ ("TMDB", "extract_movie_db"), @@ -128,6 +140,7 @@ class MediaExtractor: }, "extension": { "sources": [ + ("MediaInfo", "extract_extension"), ("FileInfo", "extract_extension"), ("Default", "extract_extension"), ], diff --git a/renamer/extractors/mediainfo_extractor.py b/renamer/extractors/mediainfo_extractor.py index 0266d6f..79b62a6 100644 --- a/renamer/extractors/mediainfo_extractor.py +++ b/renamer/extractors/mediainfo_extractor.py @@ -1,7 +1,7 @@ from pathlib import Path from pymediainfo import MediaInfo from collections import Counter -from ..constants import FRAME_CLASSES +from ..constants import FRAME_CLASSES, MEDIA_TYPES import langcodes @@ -21,6 +21,15 @@ class MediaInfoExtractor: self.audio_tracks = [] self.sub_tracks = [] + # Build mapping from meta_type to extensions + self._format_to_extensions = {} + for ext, info in MEDIA_TYPES.items(): + meta_type = info.get('meta_type') + if meta_type: + if meta_type not in self._format_to_extensions: + self._format_to_extensions[meta_type] = [] + self._format_to_extensions[meta_type].append(ext) + def _get_frame_class_from_height(self, height: int) -> str | None: """Get frame class from video height, finding closest match if exact not found""" if not height: @@ -182,4 +191,51 @@ class MediaInfoExtractor: 'format': getattr(s, 'format', None) or getattr(s, 'codec', None) or 'unknown', } tracks.append(track_data) - return tracks \ No newline at end of file + return tracks + + def is_3d(self) -> bool: + """Check if the video is 3D""" + if not self.video_tracks: + return False + multi_view = getattr(self.video_tracks[0], 'multi_view_count', None) + if multi_view and int(multi_view) > 1: + return True + stereoscopic = getattr(self.video_tracks[0], 'stereoscopic', None) + if stereoscopic == 'Yes': + return True + return False + + def extract_anamorphic(self) -> str | None: + """Extract anamorphic info for 3D videos""" + if not self.video_tracks: + return None + anamorphic = getattr(self.video_tracks[0], 'anamorphic', None) + if anamorphic == 'Yes' and self.is_3d(): + return 'Anamorphic:Yes' + return None + + def extract_extension(self) -> str | None: + """Extract file extension based on container format""" + if not self.media_info: + return None + general_track = next((t for t in self.media_info.tracks if t.track_type == 'General'), None) + if not general_track: + return None + format_ = getattr(general_track, 'format', None) + if format_ in self._format_to_extensions: + exts = self._format_to_extensions[format_] + if format_ == 'Matroska': + if self.is_3d() and 'mk3d' in exts: + return 'mk3d' + else: + return 'mkv' + else: + return exts[0] if exts else None + return None + + def extract_3d_layout(self) -> str | None: + """Extract 3D stereoscopic layout from MediaInfo""" + if not self.is_3d(): + return None + stereoscopic = getattr(self.video_tracks[0], 'stereoscopic', None) + return stereoscopic if stereoscopic else None \ No newline at end of file diff --git a/renamer/formatters/media_formatter.py b/renamer/formatters/media_formatter.py index 12e766d..ac82445 100644 --- a/renamer/formatters/media_formatter.py +++ b/renamer/formatters/media_formatter.py @@ -253,6 +253,25 @@ class MediaFormatter: or "Not extracted", "display_formatters": [TextFormatter.grey], }, + { + "label": "Anamorphic", + "label_formatters": [TextFormatter.bold], + "value": self.extractor.get("anamorphic", "MediaInfo") or "Not extracted", + "display_formatters": [TextFormatter.grey], + }, + { + "label": "Extension", + "label_formatters": [TextFormatter.bold], + "value": self.extractor.get("extension", "MediaInfo") or "Not extracted", + "value_formatters": [ExtensionFormatter.format_extension_info], + "display_formatters": [TextFormatter.grey], + }, + { + "label": "3D Layout", + "label_formatters": [TextFormatter.bold], + "value": self.extractor.get("3d_layout", "MediaInfo") or "Not extracted", + "display_formatters": [TextFormatter.grey], + }, ] return FormatterApplier.format_data_items(data) diff --git a/renamer/test/test_mediainfo_extractor.py b/renamer/test/test_mediainfo_extractor.py index a1de9d1..5d3665d 100644 --- a/renamer/test/test_mediainfo_extractor.py +++ b/renamer/test/test_mediainfo_extractor.py @@ -29,4 +29,22 @@ class TestMediaInfoExtractor: """Test extracting audio languages""" langs = extractor.extract_audio_langs() # Text files don't have audio tracks - assert langs is None \ No newline at end of file + assert langs is None + + def test_extract_anamorphic(self, extractor, test_file): + """Test extracting anamorphic info""" + anamorphic = extractor.extract_anamorphic() + # Text files don't have video tracks + assert anamorphic is None + + def test_extract_extension(self, extractor, test_file): + """Test extracting extension""" + extension = extractor.extract_extension() + # For text file, should return None since no media info + assert extension is None + + def test_is_3d(self, extractor, test_file): + """Test checking if video is 3D""" + is_3d = extractor.is_3d() + # Text files don't have video tracks + assert is_3d is False \ No newline at end of file diff --git a/uv.lock b/uv.lock index c38f3a1..156c340 100644 --- a/uv.lock +++ b/uv.lock @@ -255,7 +255,7 @@ wheels = [ [[package]] name = "renamer" -version = "0.4.6" +version = "0.4.7" source = { editable = "." } dependencies = [ { name = "langcodes" },