feat: Update version to 0.4.7 and add new extraction methods for anamorphic, extension, and 3D layout

This commit is contained in:
sHa
2025-12-28 17:06:29 +00:00
parent 75339ee7f7
commit eedc32bf31
8 changed files with 118 additions and 6 deletions

BIN
dist/renamer-0.4.7-py3-none-any.whl vendored Normal file

Binary file not shown.

View File

@@ -1,6 +1,6 @@
[project] [project]
name = "renamer" name = "renamer"
version = "0.4.6" version = "0.4.7"
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"

View File

@@ -23,7 +23,7 @@ class DefaultExtractor:
return None return None
def extract_special_info(self): def extract_special_info(self):
return [] return None
def extract_audio_langs(self): def extract_audio_langs(self):
return None return None
@@ -55,6 +55,12 @@ class DefaultExtractor:
def extract_subtitle_tracks(self): def extract_subtitle_tracks(self):
return [] return []
def extract_anamorphic(self):
return None
def extract_extension(self):
return None
def extract_tmdb_url(self): def extract_tmdb_url(self):
return None return None

View File

@@ -76,6 +76,18 @@ class MediaExtractor:
("Default", "extract_hdr"), ("Default", "extract_hdr"),
], ],
}, },
"anamorphic": {
"sources": [
("MediaInfo", "extract_anamorphic"),
("Default", "extract_anamorphic"),
],
},
"3d_layout": {
"sources": [
("MediaInfo", "extract_3d_layout"),
("Default", "extract_3d_layout"),
],
},
"movie_db": { "movie_db": {
"sources": [ "sources": [
("TMDB", "extract_movie_db"), ("TMDB", "extract_movie_db"),
@@ -128,6 +140,7 @@ class MediaExtractor:
}, },
"extension": { "extension": {
"sources": [ "sources": [
("MediaInfo", "extract_extension"),
("FileInfo", "extract_extension"), ("FileInfo", "extract_extension"),
("Default", "extract_extension"), ("Default", "extract_extension"),
], ],

View File

@@ -1,7 +1,7 @@
from pathlib import Path from pathlib import Path
from pymediainfo import MediaInfo from pymediainfo import MediaInfo
from collections import Counter from collections import Counter
from ..constants import FRAME_CLASSES from ..constants import FRAME_CLASSES, MEDIA_TYPES
import langcodes import langcodes
@@ -21,6 +21,15 @@ class MediaInfoExtractor:
self.audio_tracks = [] self.audio_tracks = []
self.sub_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: def _get_frame_class_from_height(self, height: int) -> str | None:
"""Get frame class from video height, finding closest match if exact not found""" """Get frame class from video height, finding closest match if exact not found"""
if not height: if not height:
@@ -182,4 +191,51 @@ class MediaInfoExtractor:
'format': getattr(s, 'format', None) or getattr(s, 'codec', None) or 'unknown', 'format': getattr(s, 'format', None) or getattr(s, 'codec', None) or 'unknown',
} }
tracks.append(track_data) tracks.append(track_data)
return tracks 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

View File

@@ -253,6 +253,25 @@ class MediaFormatter:
or "Not extracted", or "Not extracted",
"display_formatters": [TextFormatter.grey], "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) return FormatterApplier.format_data_items(data)

View File

@@ -29,4 +29,22 @@ class TestMediaInfoExtractor:
"""Test extracting audio languages""" """Test extracting audio languages"""
langs = extractor.extract_audio_langs() langs = extractor.extract_audio_langs()
# Text files don't have audio tracks # Text files don't have audio tracks
assert langs is None 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

2
uv.lock generated
View File

@@ -255,7 +255,7 @@ wheels = [
[[package]] [[package]]
name = "renamer" name = "renamer"
version = "0.4.6" version = "0.4.7"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "langcodes" }, { name = "langcodes" },