3 Commits
7 changed files with 47 additions and 7 deletions
+3 -3
View File
@@ -28,7 +28,7 @@ powershell -c "irm https://astral.sh/uv/install.sh | iex"
uv tool install https://github.com/shadoll/moma/releases/latest/download/moma-latest.tar.gz uv tool install https://github.com/shadoll/moma/releases/latest/download/moma-latest.tar.gz
# Specific version # Specific version
uv tool install https://github.com/shadoll/moma/releases/download/v0.9.7/moma-0.9.7-py3-none-any.whl uv tool install https://github.com/shadoll/moma/releases/download/v0.9.9/moma-0.9.9-py3-none-any.whl
# From PyPI (when published) # From PyPI (when published)
uv tool install moma uv tool install moma
@@ -40,7 +40,7 @@ uv tool install moma
uv tool install --force https://github.com/shadoll/moma/releases/latest/download/moma-latest.tar.gz uv tool install --force https://github.com/shadoll/moma/releases/latest/download/moma-latest.tar.gz
# Upgrade to a newer specific version # Upgrade to a newer specific version
uv tool install --force https://github.com/shadoll/moma/releases/download/v0.9.7/moma-0.9.7-py3-none-any.whl uv tool install --force https://github.com/shadoll/moma/releases/download/v0.9.9/moma-0.9.9-py3-none-any.whl
``` ```
#### Usage #### Usage
@@ -56,7 +56,7 @@ moma /path/to/directory # Scan specific directory
pip install https://github.com/shadoll/moma/releases/latest/download/moma-latest.tar.gz pip install https://github.com/shadoll/moma/releases/latest/download/moma-latest.tar.gz
# Specific version # Specific version
pip install https://github.com/shadoll/moma/releases/download/v0.9.7/moma-0.9.7-py3-none-any.whl pip install https://github.com/shadoll/moma/releases/download/v0.9.9/moma-0.9.9-py3-none-any.whl
``` ```
### Method 3: Development Installation ### Method 3: Development Installation
+1 -1
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "moma" name = "moma"
version = "0.9.7" version = "0.9.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.12" requires-python = ">=3.12"
+1
View File
@@ -123,6 +123,7 @@ class MediaExtractor:
"3d_layout": { "3d_layout": {
"sources": [ "sources": [
("MediaInfo", "extract_3d_layout"), ("MediaInfo", "extract_3d_layout"),
("Filename", "extract_3d_layout"),
("Default", "extract_3d_layout"), ("Default", "extract_3d_layout"),
], ],
}, },
+27
View File
@@ -251,6 +251,33 @@ class FilenameExtractor:
return None return None
@cached_method()
def extract_3d_layout(self) -> str | None:
"""Extract 3D stereoscopic layout from filename or extension"""
# Ordered by specificity: most specific patterns first
patterns = [
(r'\b3D-SBS\b', '3D-SBS'),
(r'\b3D-OU\b', '3D-OU'),
(r'\b3D-HSBS\b', '3D-HSBS'),
(r'\b3D-HOU\b', '3D-HOU'),
(r'\bHalf[-.]?SBS\b', '3D-HSBS'),
(r'\bHalf[-.]?OU\b', '3D-HOU'),
(r'\bHSBS\b', '3D-HSBS'),
(r'\bHOU\b', '3D-HOU'),
(r'\bSBS\b', '3D-SBS'),
(r'\bOU\b', '3D-OU'),
(r'\b3D\b', '3D'),
]
for pattern, result in patterns:
if re.search(pattern, self.file_name, re.IGNORECASE):
return result
# Fall back to mk3d extension as a 3D indicator
if self.file_path.suffix.lower() == '.mk3d':
return '3D'
return None
@cached_method() @cached_method()
def extract_movie_db(self) -> list[str] | None: def extract_movie_db(self) -> list[str] | None:
"""Extract movie database identifier from filename""" """Extract movie database identifier from filename"""
+7 -1
View File
@@ -213,7 +213,8 @@ class MediaInfoExtractor:
# Determine scan type from available attributes # Determine scan type from available attributes
# Check scan_type first (e.g., "Interlaced", "Progressive", "MBAFF") # Check scan_type first (e.g., "Interlaced", "Progressive", "MBAFF")
if scan_type_attr and isinstance(scan_type_attr, str): if scan_type_attr and isinstance(scan_type_attr, str):
scan_type = "i" if "interlaced" in scan_type_attr.lower() else "p" scan_lower = scan_type_attr.lower()
scan_type = "i" if ("interlaced" in scan_lower or "mbaff" in scan_lower) else "p"
logger.debug( logger.debug(
f"[{self.file_path.name}] Using scan_type: {scan_type_attr!r} -> scan_type={scan_type!r}" f"[{self.file_path.name}] Using scan_type: {scan_type_attr!r} -> scan_type={scan_type!r}"
) )
@@ -388,6 +389,11 @@ class MediaInfoExtractor:
# Use the constants function to get extension from format # Use the constants function to get extension from format
ext = get_extension_from_format(format_) ext = get_extension_from_format(format_)
# Preserve mk3d extension when the source file already has it,
# even if MediaInfo doesn't report stereoscopic metadata
if self.file_path.suffix.lower() == '.mk3d':
return 'mk3d'
# Special case: Matroska 3D uses mk3d extension # Special case: Matroska 3D uses mk3d extension
if ext == "mkv" and self.is_3d(): if ext == "mkv" and self.is_3d():
return "mk3d" return "mk3d"
+7 -1
View File
@@ -48,6 +48,12 @@ class ProposedFilenameView:
"""Get the frame class""" """Get the frame class"""
return self._extractor.get("frame_class") or "" return self._extractor.get("frame_class") or ""
@property
@conditional_decorators.wrap(",")
def _3d_layout(self) -> str:
"""Get the 3D layout formatted with a leading comma if present"""
return self._extractor.get("3d_layout")
@property @property
@conditional_decorators.wrap(",") @conditional_decorators.wrap(",")
def _hdr(self) -> str: def _hdr(self) -> str:
@@ -81,7 +87,7 @@ class ProposedFilenameView:
@property @property
def rename_line(self) -> str: def rename_line(self) -> str:
"""Generate the proposed filename.""" """Generate the proposed filename."""
result = f"{self._order}{self._title}{self._year}{self._special_info}{self._source} [{self._frame_class}{self._hdr},{self._audio_langs}]{self._db_info}.{self._extension}" result = f"{self._order}{self._title}{self._year}{self._special_info}{self._source} [{self._frame_class}{self._3d_layout}{self._hdr},{self._audio_langs}]{self._db_info}.{self._extension}"
return result.replace("/", "-").replace("\\", "-") return result.replace("/", "-").replace("\\", "-")
def rename_line_formatted(self, file_path) -> str: def rename_line_formatted(self, file_path) -> str:
Generated
+1 -1
View File
@@ -208,7 +208,7 @@ wheels = [
[[package]] [[package]]
name = "moma" name = "moma"
version = "0.9.7" version = "0.9.9"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "langcodes" }, { name = "langcodes" },