mirror of
https://github.com/shadoll/moma.git
synced 2026-08-28 03:27:34 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b50452fff0 | ||
|
|
274ce4d451 | ||
|
|
dfe3f9fe11 |
+3
-3
@@ -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
@@ -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"
|
||||||
|
|||||||
@@ -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"),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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"""
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user