Add decorators for formatting various media attributes
- Introduced `DurationDecorators` for full and short duration formatting. - Added `ExtensionDecorators` for formatting extension information. - Created `ResolutionDecorators` for formatting resolution dimensions. - Implemented `SizeDecorators` for full and short size formatting. - Enhanced `TextDecorators` with additional formatting options including blue and grey text, URL formatting, and escaping rich markup. - Developed `TrackDecorators` for formatting video, audio, and subtitle track data. - Refactored `MediaPanelView` to utilize a new `MediaPanelProperties` class for cleaner property management and formatting. - Updated `media_panel_properties.py` to include formatted properties for file info, TMDB data, metadata extraction, media info extraction, and filename extraction. - Bumped version to 0.6.5 in `uv.lock`.
This commit is contained in:
BIN
dist/renamer-0.6.4-py3-none-any.whl
vendored
Normal file
BIN
dist/renamer-0.6.4-py3-none-any.whl
vendored
Normal file
Binary file not shown.
BIN
dist/renamer-0.6.5-py3-none-any.whl
vendored
Normal file
BIN
dist/renamer-0.6.5-py3-none-any.whl
vendored
Normal file
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "renamer"
|
name = "renamer"
|
||||||
version = "0.6.3"
|
version = "0.6.5"
|
||||||
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"
|
||||||
|
|||||||
@@ -45,13 +45,9 @@ class FileInfoExtractor:
|
|||||||
Args:
|
Args:
|
||||||
file_path: Path object pointing to the file to extract info from
|
file_path: Path object pointing to the file to extract info from
|
||||||
"""
|
"""
|
||||||
self.file_path = file_path
|
self._file_path = file_path
|
||||||
self._size = file_path.stat().st_size
|
self._stat = file_path.stat()
|
||||||
self._modification_time = file_path.stat().st_mtime
|
|
||||||
self._file_name = file_path.name
|
|
||||||
self._file_path = str(file_path)
|
|
||||||
self._cache: dict[str, any] = {} # Internal cache for method results
|
self._cache: dict[str, any] = {} # Internal cache for method results
|
||||||
logging.info(f"FileInfoExtractor: file_name={self._file_name!r}, file_path={self._file_path!r}")
|
|
||||||
|
|
||||||
@cached_method()
|
@cached_method()
|
||||||
def extract_size(self) -> int:
|
def extract_size(self) -> int:
|
||||||
@@ -60,7 +56,7 @@ class FileInfoExtractor:
|
|||||||
Returns:
|
Returns:
|
||||||
File size in bytes as an integer
|
File size in bytes as an integer
|
||||||
"""
|
"""
|
||||||
return self._size
|
return self._stat.st_size
|
||||||
|
|
||||||
@cached_method()
|
@cached_method()
|
||||||
def extract_modification_time(self) -> float:
|
def extract_modification_time(self) -> float:
|
||||||
@@ -69,7 +65,7 @@ class FileInfoExtractor:
|
|||||||
Returns:
|
Returns:
|
||||||
Unix timestamp (seconds since epoch) as a float
|
Unix timestamp (seconds since epoch) as a float
|
||||||
"""
|
"""
|
||||||
return self._modification_time
|
return self._stat.st_mtime
|
||||||
|
|
||||||
@cached_method()
|
@cached_method()
|
||||||
def extract_file_name(self) -> str:
|
def extract_file_name(self) -> str:
|
||||||
@@ -78,7 +74,7 @@ class FileInfoExtractor:
|
|||||||
Returns:
|
Returns:
|
||||||
File name including extension (e.g., "movie.mkv")
|
File name including extension (e.g., "movie.mkv")
|
||||||
"""
|
"""
|
||||||
return self._file_name
|
return self._file_path.name
|
||||||
|
|
||||||
@cached_method()
|
@cached_method()
|
||||||
def extract_file_path(self) -> str:
|
def extract_file_path(self) -> str:
|
||||||
@@ -87,7 +83,7 @@ class FileInfoExtractor:
|
|||||||
Returns:
|
Returns:
|
||||||
Absolute file path as a string
|
Absolute file path as a string
|
||||||
"""
|
"""
|
||||||
return self._file_path
|
return str(self._file_path)
|
||||||
|
|
||||||
@cached_method()
|
@cached_method()
|
||||||
def extract_extension(self) -> str:
|
def extract_extension(self) -> str:
|
||||||
@@ -96,4 +92,4 @@ class FileInfoExtractor:
|
|||||||
Returns:
|
Returns:
|
||||||
File extension in lowercase without leading dot (e.g., "mkv", "mp4")
|
File extension in lowercase without leading dot (e.g., "mkv", "mp4")
|
||||||
"""
|
"""
|
||||||
return self.file_path.suffix.lower().lstrip('.')
|
return self._file_path.suffix.lower().lstrip('.')
|
||||||
@@ -28,6 +28,11 @@ from .date_decorators import date_decorators, DateDecorators
|
|||||||
from .special_info_decorators import special_info_decorators, SpecialInfoDecorators
|
from .special_info_decorators import special_info_decorators, SpecialInfoDecorators
|
||||||
from .text_decorators import text_decorators, TextDecorators
|
from .text_decorators import text_decorators, TextDecorators
|
||||||
from .conditional_decorators import conditional_decorators, ConditionalDecorators
|
from .conditional_decorators import conditional_decorators, ConditionalDecorators
|
||||||
|
from .size_decorators import size_decorators, SizeDecorators
|
||||||
|
from .extension_decorators import extension_decorators, ExtensionDecorators
|
||||||
|
from .duration_decorators import duration_decorators, DurationDecorators
|
||||||
|
from .resolution_decorators import resolution_decorators, ResolutionDecorators
|
||||||
|
from .track_decorators import track_decorators, TrackDecorators
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
# Base classes
|
# Base classes
|
||||||
@@ -57,4 +62,14 @@ __all__ = [
|
|||||||
'TextDecorators',
|
'TextDecorators',
|
||||||
'conditional_decorators',
|
'conditional_decorators',
|
||||||
'ConditionalDecorators',
|
'ConditionalDecorators',
|
||||||
|
'size_decorators',
|
||||||
|
'SizeDecorators',
|
||||||
|
'extension_decorators',
|
||||||
|
'ExtensionDecorators',
|
||||||
|
'duration_decorators',
|
||||||
|
'DurationDecorators',
|
||||||
|
'resolution_decorators',
|
||||||
|
'ResolutionDecorators',
|
||||||
|
'track_decorators',
|
||||||
|
'TrackDecorators',
|
||||||
]
|
]
|
||||||
@@ -19,6 +19,7 @@ class ConditionalDecorators:
|
|||||||
"""Decorator to wrap value with delimiters if it exists.
|
"""Decorator to wrap value with delimiters if it exists.
|
||||||
|
|
||||||
Can be used for prefix-only (right=""), suffix-only (left=""), or both.
|
Can be used for prefix-only (right=""), suffix-only (left=""), or both.
|
||||||
|
Supports format string placeholders that will be filled from function arguments.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
@conditional_decorators.wrap("[", "]")
|
@conditional_decorators.wrap("[", "]")
|
||||||
@@ -34,12 +35,40 @@ class ConditionalDecorators:
|
|||||||
@conditional_decorators.wrap("", ",")
|
@conditional_decorators.wrap("", ",")
|
||||||
def get_hdr(self):
|
def get_hdr(self):
|
||||||
return self.extractor.get('hdr')
|
return self.extractor.get('hdr')
|
||||||
|
|
||||||
|
# With placeholders
|
||||||
|
@conditional_decorators.wrap("Track {index}: ")
|
||||||
|
def get_track(self, data, index):
|
||||||
|
return data
|
||||||
"""
|
"""
|
||||||
def decorator(func: Callable) -> Callable:
|
def decorator(func: Callable) -> Callable:
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
def wrapper(*args, **kwargs) -> str:
|
def wrapper(*args, **kwargs) -> str:
|
||||||
result = func(*args, **kwargs)
|
result = func(*args, **kwargs)
|
||||||
return f"{left}{result}{right}" if result else ""
|
if not result:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
# Extract format arguments from function signature
|
||||||
|
# Skip 'self' (args[0]) and the main data argument
|
||||||
|
format_kwargs = {}
|
||||||
|
if len(args) > 2: # self, data, index, ...
|
||||||
|
# Try to detect named parameters from function signature
|
||||||
|
import inspect
|
||||||
|
sig = inspect.signature(func)
|
||||||
|
param_names = list(sig.parameters.keys())
|
||||||
|
# Skip first two params (self, data/track/value)
|
||||||
|
for i, param_name in enumerate(param_names[2:], start=2):
|
||||||
|
if i < len(args):
|
||||||
|
format_kwargs[param_name] = args[i]
|
||||||
|
|
||||||
|
# Also add explicit kwargs
|
||||||
|
format_kwargs.update(kwargs)
|
||||||
|
|
||||||
|
# Format left and right with available arguments
|
||||||
|
formatted_left = left.format(**format_kwargs) if format_kwargs else left
|
||||||
|
formatted_right = right.format(**format_kwargs) if format_kwargs else right
|
||||||
|
|
||||||
|
return f"{formatted_left}{result}{formatted_right}"
|
||||||
return wrapper
|
return wrapper
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|||||||
42
renamer/formatters/duration_decorators.py
Normal file
42
renamer/formatters/duration_decorators.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
"""Duration formatting decorators.
|
||||||
|
|
||||||
|
Provides decorator versions of DurationFormatter methods.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from functools import wraps
|
||||||
|
from typing import Callable
|
||||||
|
from .duration_formatter import DurationFormatter
|
||||||
|
|
||||||
|
|
||||||
|
class DurationDecorators:
|
||||||
|
"""Duration formatting decorators."""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def duration_full() -> Callable:
|
||||||
|
"""Decorator to format duration in full format (HH:MM:SS)."""
|
||||||
|
def decorator(func: Callable) -> Callable:
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
if not result:
|
||||||
|
return ""
|
||||||
|
return DurationFormatter.format_full(result)
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def duration_short() -> Callable:
|
||||||
|
"""Decorator to format duration in short format."""
|
||||||
|
def decorator(func: Callable) -> Callable:
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
if not result:
|
||||||
|
return ""
|
||||||
|
return DurationFormatter.format_short(result)
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
# Singleton instance
|
||||||
|
duration_decorators = DurationDecorators()
|
||||||
29
renamer/formatters/extension_decorators.py
Normal file
29
renamer/formatters/extension_decorators.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
"""Extension formatting decorators.
|
||||||
|
|
||||||
|
Provides decorator versions of ExtensionFormatter methods.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from functools import wraps
|
||||||
|
from typing import Callable
|
||||||
|
from .extension_formatter import ExtensionFormatter
|
||||||
|
|
||||||
|
|
||||||
|
class ExtensionDecorators:
|
||||||
|
"""Extension formatting decorators."""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def extension_info() -> Callable:
|
||||||
|
"""Decorator to format extension information."""
|
||||||
|
def decorator(func: Callable) -> Callable:
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
if not result:
|
||||||
|
return ""
|
||||||
|
return ExtensionFormatter.format_extension_info(result)
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
# Singleton instance
|
||||||
|
extension_decorators = ExtensionDecorators()
|
||||||
29
renamer/formatters/resolution_decorators.py
Normal file
29
renamer/formatters/resolution_decorators.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
"""Resolution formatting decorators.
|
||||||
|
|
||||||
|
Provides decorator versions of ResolutionFormatter methods.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from functools import wraps
|
||||||
|
from typing import Callable
|
||||||
|
from .resolution_formatter import ResolutionFormatter
|
||||||
|
|
||||||
|
|
||||||
|
class ResolutionDecorators:
|
||||||
|
"""Resolution formatting decorators."""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolution_dimensions() -> Callable:
|
||||||
|
"""Decorator to format resolution as dimensions (WxH)."""
|
||||||
|
def decorator(func: Callable) -> Callable:
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
if not result:
|
||||||
|
return ""
|
||||||
|
return ResolutionFormatter.format_resolution_dimensions(result)
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
# Singleton instance
|
||||||
|
resolution_decorators = ResolutionDecorators()
|
||||||
42
renamer/formatters/size_decorators.py
Normal file
42
renamer/formatters/size_decorators.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
"""Size formatting decorators.
|
||||||
|
|
||||||
|
Provides decorator versions of SizeFormatter methods.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from functools import wraps
|
||||||
|
from typing import Callable
|
||||||
|
from .size_formatter import SizeFormatter
|
||||||
|
|
||||||
|
|
||||||
|
class SizeDecorators:
|
||||||
|
"""Size formatting decorators."""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def size_full() -> Callable:
|
||||||
|
"""Decorator to format file size in full format."""
|
||||||
|
def decorator(func: Callable) -> Callable:
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
if result is None:
|
||||||
|
return ""
|
||||||
|
return SizeFormatter.format_size_full(result)
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def size_short() -> Callable:
|
||||||
|
"""Decorator to format file size in short format."""
|
||||||
|
def decorator(func: Callable) -> Callable:
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
if result is None:
|
||||||
|
return ""
|
||||||
|
return SizeFormatter.format_size_short(result)
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
# Singleton instance
|
||||||
|
size_decorators = SizeDecorators()
|
||||||
@@ -22,6 +22,8 @@ class TextDecorators:
|
|||||||
@wraps(func)
|
@wraps(func)
|
||||||
def wrapper(*args, **kwargs) -> str:
|
def wrapper(*args, **kwargs) -> str:
|
||||||
result = func(*args, **kwargs)
|
result = func(*args, **kwargs)
|
||||||
|
if result == "":
|
||||||
|
return ""
|
||||||
return TextFormatter.bold(str(result))
|
return TextFormatter.bold(str(result))
|
||||||
return wrapper
|
return wrapper
|
||||||
return decorator
|
return decorator
|
||||||
@@ -33,6 +35,8 @@ class TextDecorators:
|
|||||||
@wraps(func)
|
@wraps(func)
|
||||||
def wrapper(*args, **kwargs) -> str:
|
def wrapper(*args, **kwargs) -> str:
|
||||||
result = func(*args, **kwargs)
|
result = func(*args, **kwargs)
|
||||||
|
if result == "":
|
||||||
|
return ""
|
||||||
return TextFormatter.italic(str(result))
|
return TextFormatter.italic(str(result))
|
||||||
return wrapper
|
return wrapper
|
||||||
return decorator
|
return decorator
|
||||||
@@ -44,6 +48,8 @@ class TextDecorators:
|
|||||||
@wraps(func)
|
@wraps(func)
|
||||||
def wrapper(*args, **kwargs) -> str:
|
def wrapper(*args, **kwargs) -> str:
|
||||||
result = func(*args, **kwargs)
|
result = func(*args, **kwargs)
|
||||||
|
if result == "":
|
||||||
|
return ""
|
||||||
return TextFormatter.green(str(result))
|
return TextFormatter.green(str(result))
|
||||||
return wrapper
|
return wrapper
|
||||||
return decorator
|
return decorator
|
||||||
@@ -55,6 +61,8 @@ class TextDecorators:
|
|||||||
@wraps(func)
|
@wraps(func)
|
||||||
def wrapper(*args, **kwargs) -> str:
|
def wrapper(*args, **kwargs) -> str:
|
||||||
result = func(*args, **kwargs)
|
result = func(*args, **kwargs)
|
||||||
|
if not result:
|
||||||
|
return ""
|
||||||
return TextFormatter.yellow(str(result))
|
return TextFormatter.yellow(str(result))
|
||||||
return wrapper
|
return wrapper
|
||||||
return decorator
|
return decorator
|
||||||
@@ -66,6 +74,8 @@ class TextDecorators:
|
|||||||
@wraps(func)
|
@wraps(func)
|
||||||
def wrapper(*args, **kwargs) -> str:
|
def wrapper(*args, **kwargs) -> str:
|
||||||
result = func(*args, **kwargs)
|
result = func(*args, **kwargs)
|
||||||
|
if not result:
|
||||||
|
return ""
|
||||||
return TextFormatter.cyan(str(result))
|
return TextFormatter.cyan(str(result))
|
||||||
return wrapper
|
return wrapper
|
||||||
return decorator
|
return decorator
|
||||||
@@ -77,6 +87,8 @@ class TextDecorators:
|
|||||||
@wraps(func)
|
@wraps(func)
|
||||||
def wrapper(*args, **kwargs) -> str:
|
def wrapper(*args, **kwargs) -> str:
|
||||||
result = func(*args, **kwargs)
|
result = func(*args, **kwargs)
|
||||||
|
if not result:
|
||||||
|
return ""
|
||||||
return TextFormatter.magenta(str(result))
|
return TextFormatter.magenta(str(result))
|
||||||
return wrapper
|
return wrapper
|
||||||
return decorator
|
return decorator
|
||||||
@@ -88,10 +100,12 @@ class TextDecorators:
|
|||||||
@wraps(func)
|
@wraps(func)
|
||||||
def wrapper(*args, **kwargs) -> str:
|
def wrapper(*args, **kwargs) -> str:
|
||||||
result = func(*args, **kwargs)
|
result = func(*args, **kwargs)
|
||||||
|
if not result:
|
||||||
|
return ""
|
||||||
return TextFormatter.red(str(result))
|
return TextFormatter.red(str(result))
|
||||||
return wrapper
|
return wrapper
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def orange() -> Callable:
|
def orange() -> Callable:
|
||||||
"""Decorator to color text orange."""
|
"""Decorator to color text orange."""
|
||||||
@@ -99,10 +113,38 @@ class TextDecorators:
|
|||||||
@wraps(func)
|
@wraps(func)
|
||||||
def wrapper(*args, **kwargs) -> str:
|
def wrapper(*args, **kwargs) -> str:
|
||||||
result = func(*args, **kwargs)
|
result = func(*args, **kwargs)
|
||||||
|
if not result:
|
||||||
|
return ""
|
||||||
return TextFormatter.orange(str(result))
|
return TextFormatter.orange(str(result))
|
||||||
return wrapper
|
return wrapper
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def blue() -> Callable:
|
||||||
|
"""Decorator to color text blue."""
|
||||||
|
def decorator(func: Callable) -> Callable:
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs) -> str:
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
if not result:
|
||||||
|
return ""
|
||||||
|
return TextFormatter.blue(str(result))
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def grey() -> Callable:
|
||||||
|
"""Decorator to color text grey."""
|
||||||
|
def decorator(func: Callable) -> Callable:
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs) -> str:
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
if not result:
|
||||||
|
return ""
|
||||||
|
return TextFormatter.grey(str(result))
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def uppercase() -> Callable:
|
def uppercase() -> Callable:
|
||||||
"""Decorator to convert text to uppercase."""
|
"""Decorator to convert text to uppercase."""
|
||||||
@@ -110,6 +152,8 @@ class TextDecorators:
|
|||||||
@wraps(func)
|
@wraps(func)
|
||||||
def wrapper(*args, **kwargs) -> str:
|
def wrapper(*args, **kwargs) -> str:
|
||||||
result = func(*args, **kwargs)
|
result = func(*args, **kwargs)
|
||||||
|
if not result:
|
||||||
|
return ""
|
||||||
return TextFormatter.uppercase(str(result))
|
return TextFormatter.uppercase(str(result))
|
||||||
return wrapper
|
return wrapper
|
||||||
return decorator
|
return decorator
|
||||||
@@ -121,10 +165,39 @@ class TextDecorators:
|
|||||||
@wraps(func)
|
@wraps(func)
|
||||||
def wrapper(*args, **kwargs) -> str:
|
def wrapper(*args, **kwargs) -> str:
|
||||||
result = func(*args, **kwargs)
|
result = func(*args, **kwargs)
|
||||||
|
if not result:
|
||||||
|
return ""
|
||||||
return TextFormatter.lowercase(str(result))
|
return TextFormatter.lowercase(str(result))
|
||||||
return wrapper
|
return wrapper
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def url() -> Callable:
|
||||||
|
"""Decorator to format text as a clickable URL."""
|
||||||
|
def decorator(func: Callable) -> Callable:
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs) -> str:
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
if not result:
|
||||||
|
return ""
|
||||||
|
return TextFormatter.format_url(str(result))
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def escape() -> Callable:
|
||||||
|
"""Decorator to escape rich markup in text."""
|
||||||
|
def decorator(func: Callable) -> Callable:
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs) -> str:
|
||||||
|
from rich.markup import escape
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
if not result:
|
||||||
|
return ""
|
||||||
|
return escape(str(result))
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
# Singleton instance
|
# Singleton instance
|
||||||
text_decorators = TextDecorators()
|
text_decorators = TextDecorators()
|
||||||
|
|||||||
55
renamer/formatters/track_decorators.py
Normal file
55
renamer/formatters/track_decorators.py
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
"""Track formatting decorators.
|
||||||
|
|
||||||
|
Provides decorator versions of TrackFormatter methods.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from functools import wraps
|
||||||
|
from typing import Callable
|
||||||
|
from .track_formatter import TrackFormatter
|
||||||
|
|
||||||
|
|
||||||
|
class TrackDecorators:
|
||||||
|
"""Track formatting decorators."""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def video_track() -> Callable:
|
||||||
|
"""Decorator to format video track data."""
|
||||||
|
def decorator(func: Callable) -> Callable:
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
if not result:
|
||||||
|
return ""
|
||||||
|
return TrackFormatter.format_video_track(result)
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def audio_track() -> Callable:
|
||||||
|
"""Decorator to format audio track data."""
|
||||||
|
def decorator(func: Callable) -> Callable:
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
if not result:
|
||||||
|
return ""
|
||||||
|
return TrackFormatter.format_audio_track(result)
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def subtitle_track() -> Callable:
|
||||||
|
"""Decorator to format subtitle track data."""
|
||||||
|
def decorator(func: Callable) -> Callable:
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
if not result:
|
||||||
|
return ""
|
||||||
|
return TrackFormatter.format_subtitle_track(result)
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
# Singleton instance
|
||||||
|
track_decorators = TrackDecorators()
|
||||||
@@ -1,14 +1,5 @@
|
|||||||
from pathlib import Path
|
from .media_panel_properties import MediaPanelProperties
|
||||||
from rich.markup import escape
|
from ..formatters.conditional_decorators import conditional_decorators
|
||||||
from ..formatters.size_formatter import SizeFormatter
|
|
||||||
from ..formatters.date_formatter import DateFormatter
|
|
||||||
from ..formatters.extension_formatter import ExtensionFormatter
|
|
||||||
from ..formatters.text_formatter import TextFormatter
|
|
||||||
from ..formatters.track_formatter import TrackFormatter
|
|
||||||
from ..formatters.resolution_formatter import ResolutionFormatter
|
|
||||||
from ..formatters.duration_formatter import DurationFormatter
|
|
||||||
from ..formatters.special_info_formatter import SpecialInfoFormatter
|
|
||||||
from ..formatters.formatter import FormatterApplier
|
|
||||||
|
|
||||||
|
|
||||||
class MediaPanelView:
|
class MediaPanelView:
|
||||||
@@ -20,410 +11,125 @@ class MediaPanelView:
|
|||||||
|
|
||||||
def __init__(self, extractor):
|
def __init__(self, extractor):
|
||||||
self.extractor = extractor
|
self.extractor = extractor
|
||||||
|
self._props = MediaPanelProperties(extractor)
|
||||||
|
|
||||||
def file_info_panel(self) -> str:
|
def file_info_panel(self) -> str:
|
||||||
"""Return formatted file info panel string"""
|
"""Return formatted file info panel string"""
|
||||||
sections = [
|
return "\n".join(
|
||||||
self.file_info(),
|
[
|
||||||
self.selected_data(),
|
self.fileinfo_section(),
|
||||||
self.tmdb_data(),
|
self.selected_section(),
|
||||||
self.tracks_info(),
|
self.tmdb_section(),
|
||||||
self.filename_extracted_data(),
|
self.tracksinfo_section(),
|
||||||
self.metadata_extracted_data(),
|
self.filename_section(),
|
||||||
self.mediainfo_extracted_data(),
|
self.metadata_section(),
|
||||||
]
|
self.mediainfo_section(),
|
||||||
return "\n\n".join("\n".join(section) for section in sections)
|
]
|
||||||
|
)
|
||||||
|
|
||||||
def file_info(self) -> list[str]:
|
@conditional_decorators.wrap("", "\n")
|
||||||
data = [
|
def fileinfo_section(self) -> str:
|
||||||
{
|
"""Return formatted file info"""
|
||||||
"group": "File Info",
|
return "\n".join(
|
||||||
"label": "File Info",
|
[
|
||||||
"label_formatters": [TextFormatter.bold, TextFormatter.uppercase],
|
self._props.title("File Info"),
|
||||||
},
|
self._props.file_path,
|
||||||
{
|
self._props.file_size,
|
||||||
"group": "File Info",
|
self._props.file_name,
|
||||||
"label": "Path",
|
self._props.modification_time,
|
||||||
"label_formatters": [TextFormatter.bold],
|
self._props.extension_fileinfo,
|
||||||
"value": escape(str(self.extractor.get("file_path", "FileInfo"))),
|
]
|
||||||
"display_formatters": [TextFormatter.blue],
|
)
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "File Info",
|
|
||||||
"label": "Size",
|
|
||||||
"value": self.extractor.get("file_size", "FileInfo"),
|
|
||||||
"value_formatters": [SizeFormatter.format_size_full],
|
|
||||||
"display_formatters": [TextFormatter.bold, TextFormatter.green],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "File Info",
|
|
||||||
"label": "Name",
|
|
||||||
"label_formatters": [TextFormatter.bold],
|
|
||||||
"value": escape(str(self.extractor.get("file_name", "FileInfo"))),
|
|
||||||
"display_formatters": [TextFormatter.cyan],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "File Info",
|
|
||||||
"label": "Modified",
|
|
||||||
"label_formatters": [TextFormatter.bold],
|
|
||||||
"value": self.extractor.get("modification_time", "FileInfo"),
|
|
||||||
"value_formatters": [DateFormatter.format_modification_date],
|
|
||||||
"display_formatters": [TextFormatter.bold, TextFormatter.magenta],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "File Info",
|
|
||||||
"label": "Extension",
|
|
||||||
"label_formatters": [TextFormatter.bold],
|
|
||||||
"value": self.extractor.get("extension", "FileInfo"),
|
|
||||||
"value_formatters": [ExtensionFormatter.format_extension_info],
|
|
||||||
"display_formatters": [TextFormatter.green],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
return FormatterApplier.format_data_items(data)
|
|
||||||
|
|
||||||
def tmdb_data(self) -> list[str]:
|
@conditional_decorators.wrap("", "\n")
|
||||||
|
def selected_section(self) -> str:
|
||||||
|
"""Return formatted selected data"""
|
||||||
|
return "\n".join(
|
||||||
|
[
|
||||||
|
self._props.title("Selected Data"),
|
||||||
|
self._props.selected_order,
|
||||||
|
self._props.selected_title,
|
||||||
|
self._props.selected_year,
|
||||||
|
self._props.selected_special_info,
|
||||||
|
self._props.selected_source,
|
||||||
|
self._props.selected_frame_class,
|
||||||
|
self._props.selected_hdr,
|
||||||
|
self._props.selected_audio_langs,
|
||||||
|
self._props.selected_database_info,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
@conditional_decorators.wrap("", "\n")
|
||||||
|
def tmdb_section(self) -> str:
|
||||||
"""Return formatted TMDB data"""
|
"""Return formatted TMDB data"""
|
||||||
data = [
|
return "\n".join(
|
||||||
{
|
[
|
||||||
"label": "TMDB Data",
|
self._props.title("TMDB Data"),
|
||||||
"label_formatters": [TextFormatter.bold, TextFormatter.uppercase],
|
self._props.tmdb_id,
|
||||||
},
|
self._props.tmdb_title,
|
||||||
{
|
self._props.tmdb_original_title,
|
||||||
"label": "ID",
|
self._props.tmdb_year,
|
||||||
"label_formatters": [TextFormatter.bold, TextFormatter.blue],
|
self._props.tmdb_database_info,
|
||||||
"value": self.extractor.get("tmdb_id", "TMDB") or "<None>",
|
self._props.tmdb_url,
|
||||||
"value_formatters": [TextFormatter.yellow],
|
]
|
||||||
},
|
)
|
||||||
{
|
|
||||||
"label": "Title",
|
|
||||||
"label_formatters": [TextFormatter.bold, TextFormatter.blue],
|
|
||||||
"value": self.extractor.get("title", "TMDB") or "<None>",
|
|
||||||
"value_formatters": [TextFormatter.yellow],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Original Title",
|
|
||||||
"label_formatters": [TextFormatter.bold, TextFormatter.blue],
|
|
||||||
"value": self.extractor.get("original_title", "TMDB") or "<None>",
|
|
||||||
"value_formatters": [TextFormatter.yellow],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Year",
|
|
||||||
"label_formatters": [TextFormatter.bold, TextFormatter.blue],
|
|
||||||
"value": self.extractor.get("year", "TMDB") or "<None>",
|
|
||||||
"value_formatters": [TextFormatter.yellow,],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Database Info",
|
|
||||||
"label_formatters": [TextFormatter.bold, TextFormatter.blue],
|
|
||||||
"value": self.extractor.get("movie_db", "TMDB") or "<None>",
|
|
||||||
"value_formatters": [SpecialInfoFormatter.format_database_info, TextFormatter.yellow],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "URL",
|
|
||||||
"label_formatters": [TextFormatter.bold, TextFormatter.blue],
|
|
||||||
"value": self.extractor.get("tmdb_url", "TMDB") or "<None>",
|
|
||||||
"value_formatters": [TextFormatter.format_url],
|
|
||||||
}
|
|
||||||
]
|
|
||||||
return FormatterApplier.format_data_items(data)
|
|
||||||
|
|
||||||
def tracks_info(self) -> list[str]:
|
@conditional_decorators.wrap("", "\n")
|
||||||
"""Return formatted tracks information"""
|
def tracksinfo_section(self) -> str:
|
||||||
data = [
|
"""Return formatted tracks information panel"""
|
||||||
{
|
return "\n".join(
|
||||||
"group": "Tracks Info",
|
[
|
||||||
"label": "Tracks Info",
|
self._props.title("Tracks Info"),
|
||||||
"label_formatters": [TextFormatter.bold, TextFormatter.uppercase],
|
*self._props.video_tracks,
|
||||||
}
|
*self._props.audio_tracks,
|
||||||
]
|
*self._props.subtitle_tracks,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
# Get video tracks
|
@conditional_decorators.wrap("", "\n")
|
||||||
video_tracks = self.extractor.get("video_tracks", "MediaInfo") or []
|
def filename_section(self) -> str:
|
||||||
for item in video_tracks:
|
|
||||||
data.append(
|
|
||||||
{
|
|
||||||
"group": "Tracks Info",
|
|
||||||
"label": "Video Track",
|
|
||||||
"value": item,
|
|
||||||
"value_formatters": TrackFormatter.format_video_track,
|
|
||||||
"display_formatters": [TextFormatter.green],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Get audio tracks
|
|
||||||
audio_tracks = self.extractor.get("audio_tracks", "MediaInfo") or []
|
|
||||||
for i, item in enumerate(audio_tracks, start=1):
|
|
||||||
data.append(
|
|
||||||
{
|
|
||||||
"group": "Tracks Info",
|
|
||||||
"label": f"Audio Track {i}",
|
|
||||||
"value": item,
|
|
||||||
"value_formatters": TrackFormatter.format_audio_track,
|
|
||||||
"display_formatters": [TextFormatter.yellow],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Get subtitle tracks
|
|
||||||
subtitle_tracks = self.extractor.get("subtitle_tracks", "MediaInfo") or []
|
|
||||||
for i, item in enumerate(subtitle_tracks, start=1):
|
|
||||||
data.append(
|
|
||||||
{
|
|
||||||
"group": "Tracks Info",
|
|
||||||
"label": f"Subtitle Track {i}",
|
|
||||||
"value": item,
|
|
||||||
"value_formatters": TrackFormatter.format_subtitle_track,
|
|
||||||
"display_formatters": [TextFormatter.magenta],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return FormatterApplier.format_data_items(data)
|
|
||||||
|
|
||||||
def metadata_extracted_data(self) -> list[str]:
|
|
||||||
"""Format metadata extraction data for the metadata panel"""
|
|
||||||
data = [
|
|
||||||
{
|
|
||||||
"label": "Metadata Extraction",
|
|
||||||
"label_formatters": [TextFormatter.bold, TextFormatter.uppercase],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Title",
|
|
||||||
"label_formatters": [TextFormatter.bold],
|
|
||||||
"value": self.extractor.get("title", "Metadata") or "Not extracted",
|
|
||||||
"display_formatters": [TextFormatter.grey],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Duration",
|
|
||||||
"label_formatters": [TextFormatter.bold],
|
|
||||||
"value": self.extractor.get("duration", "Metadata") or "Not extracted",
|
|
||||||
"value_formatters": [DurationFormatter.format_full],
|
|
||||||
"display_formatters": [TextFormatter.grey],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Artist",
|
|
||||||
"label_formatters": [TextFormatter.bold],
|
|
||||||
"value": self.extractor.get("artist", "Metadata") or "Not extracted",
|
|
||||||
"display_formatters": [TextFormatter.grey],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
return FormatterApplier.format_data_items(data)
|
|
||||||
|
|
||||||
def mediainfo_extracted_data(self) -> list[str]:
|
|
||||||
"""Format media info extraction data for the mediainfo panel"""
|
|
||||||
data = [
|
|
||||||
{
|
|
||||||
"label": "Media Info Extraction",
|
|
||||||
"label_formatters": [TextFormatter.bold, TextFormatter.uppercase],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Duration",
|
|
||||||
"label_formatters": [TextFormatter.bold],
|
|
||||||
"value": self.extractor.get("duration", "MediaInfo") or "Not extracted",
|
|
||||||
"value_formatters": [DurationFormatter.format_full],
|
|
||||||
"display_formatters": [TextFormatter.grey],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Frame Class",
|
|
||||||
"label_formatters": [TextFormatter.bold],
|
|
||||||
"value": self.extractor.get("frame_class", "MediaInfo")
|
|
||||||
or "Not extracted",
|
|
||||||
"display_formatters": [TextFormatter.grey],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Resolution",
|
|
||||||
"label_formatters": [TextFormatter.bold],
|
|
||||||
"value": self.extractor.get("resolution", "MediaInfo")
|
|
||||||
or "Not extracted",
|
|
||||||
"value_formatters": [ResolutionFormatter.format_resolution_dimensions],
|
|
||||||
"display_formatters": [TextFormatter.grey],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Aspect Ratio",
|
|
||||||
"label_formatters": [TextFormatter.bold],
|
|
||||||
"value": self.extractor.get("aspect_ratio", "MediaInfo")
|
|
||||||
or "Not extracted",
|
|
||||||
"display_formatters": [TextFormatter.grey],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "HDR",
|
|
||||||
"label_formatters": [TextFormatter.bold],
|
|
||||||
"value": self.extractor.get("hdr", "MediaInfo") or "Not extracted",
|
|
||||||
"display_formatters": [TextFormatter.grey],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Audio Languages",
|
|
||||||
"label_formatters": [TextFormatter.bold],
|
|
||||||
"value": self.extractor.get("audio_langs", "MediaInfo")
|
|
||||||
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)
|
|
||||||
|
|
||||||
def filename_extracted_data(self) -> list[str]:
|
|
||||||
"""Return formatted filename extracted data"""
|
"""Return formatted filename extracted data"""
|
||||||
data = [
|
return "\n".join(
|
||||||
{
|
[
|
||||||
"label": "Filename Extracted Data",
|
self._props.title("Filename Extracted Data"),
|
||||||
"label_formatters": [TextFormatter.bold, TextFormatter.uppercase],
|
self._props.filename_order,
|
||||||
},
|
self._props.filename_title,
|
||||||
{
|
self._props.filename_year,
|
||||||
"label": "Order",
|
self._props.filename_source,
|
||||||
"label_formatters": [TextFormatter.bold],
|
self._props.filename_frame_class,
|
||||||
"value": self.extractor.get("order", "Filename") or "Not extracted",
|
self._props.filename_hdr,
|
||||||
"display_formatters": [TextFormatter.yellow],
|
self._props.filename_audio_langs,
|
||||||
},
|
self._props.filename_special_info,
|
||||||
{
|
self._props.filename_movie_db,
|
||||||
"label": "Movie title",
|
]
|
||||||
"label_formatters": [TextFormatter.bold],
|
)
|
||||||
"value": self.extractor.get("title", "Filename"),
|
|
||||||
"display_formatters": [TextFormatter.grey],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Year",
|
|
||||||
"label_formatters": [TextFormatter.bold],
|
|
||||||
"value": self.extractor.get("year", "Filename"),
|
|
||||||
"display_formatters": [TextFormatter.grey],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Video source",
|
|
||||||
"label_formatters": [TextFormatter.bold],
|
|
||||||
"value": self.extractor.get("source", "Filename") or "Not extracted",
|
|
||||||
"display_formatters": [TextFormatter.grey],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Frame class",
|
|
||||||
"label_formatters": [TextFormatter.bold],
|
|
||||||
"value": self.extractor.get("frame_class", "Filename")
|
|
||||||
or "Not extracted",
|
|
||||||
"display_formatters": [TextFormatter.grey],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "HDR",
|
|
||||||
"label_formatters": [TextFormatter.bold],
|
|
||||||
"value": self.extractor.get("hdr", "Filename") or "Not extracted",
|
|
||||||
"display_formatters": [TextFormatter.grey],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Audio langs",
|
|
||||||
"label_formatters": [TextFormatter.bold],
|
|
||||||
"value": self.extractor.get("audio_langs", "Filename")
|
|
||||||
or "Not extracted",
|
|
||||||
"display_formatters": [TextFormatter.grey],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Special info",
|
|
||||||
"label_formatters": [TextFormatter.bold],
|
|
||||||
"value": self.extractor.get("special_info", "Filename")
|
|
||||||
or "Not extracted",
|
|
||||||
"value_formatters": [
|
|
||||||
SpecialInfoFormatter.format_special_info,
|
|
||||||
TextFormatter.blue,
|
|
||||||
],
|
|
||||||
"display_formatters": [TextFormatter.grey],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Movie DB",
|
|
||||||
"label_formatters": [TextFormatter.bold],
|
|
||||||
"value": self.extractor.get("movie_db", "Filename") or "Not extracted",
|
|
||||||
"display_formatters": [TextFormatter.grey],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
return FormatterApplier.format_data_items(data)
|
@conditional_decorators.wrap("", "\n")
|
||||||
|
def metadata_section(self) -> str:
|
||||||
|
"""Return formatted metadata extraction data"""
|
||||||
|
return "\n".join(
|
||||||
|
[
|
||||||
|
self._props.title("Metadata Extraction"),
|
||||||
|
self._props.metadata_title,
|
||||||
|
self._props.metadata_duration,
|
||||||
|
self._props.metadata_artist,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
def selected_data(self) -> list[str]:
|
@conditional_decorators.wrap("", "\n")
|
||||||
"""Return formatted selected data string"""
|
def mediainfo_section(self) -> str:
|
||||||
import logging
|
"""Return formatted media info extraction data"""
|
||||||
import os
|
return "\n".join(
|
||||||
if os.getenv("FORMATTER_LOG"):
|
[
|
||||||
frame_class = self.extractor.get("frame_class")
|
self._props.title("Media Info Extraction"),
|
||||||
audio_langs = self.extractor.get("audio_langs")
|
self._props.mediainfo_duration,
|
||||||
logging.info(f"Selected data - frame_class: {frame_class!r}, audio_langs: {audio_langs!r}")
|
self._props.mediainfo_frame_class,
|
||||||
# Also check from Filename source
|
self._props.mediainfo_resolution,
|
||||||
frame_class_filename = self.extractor.get("frame_class", "Filename")
|
self._props.mediainfo_aspect_ratio,
|
||||||
audio_langs_filename = self.extractor.get("audio_langs", "Filename")
|
self._props.mediainfo_hdr,
|
||||||
logging.info(f"From Filename - frame_class: {frame_class_filename!r}, audio_langs: {audio_langs_filename!r}")
|
self._props.mediainfo_audio_langs,
|
||||||
data = [
|
self._props.mediainfo_anamorphic,
|
||||||
{
|
self._props.mediainfo_extension,
|
||||||
"label": "Selected Data",
|
self._props.mediainfo_3d_layout,
|
||||||
"label_formatters": [TextFormatter.bold, TextFormatter.uppercase],
|
]
|
||||||
},
|
)
|
||||||
{
|
|
||||||
"label": "Order",
|
|
||||||
"label_formatters": [TextFormatter.bold, TextFormatter.blue],
|
|
||||||
"value": self.extractor.get("order") or "<None>",
|
|
||||||
"value_formatters": [TextFormatter.yellow],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Title",
|
|
||||||
"label_formatters": [TextFormatter.bold, TextFormatter.blue],
|
|
||||||
"value": self.extractor.get("title") or "<None>",
|
|
||||||
"value_formatters": [TextFormatter.yellow],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Year",
|
|
||||||
"label_formatters": [TextFormatter.bold, TextFormatter.blue],
|
|
||||||
"value": self.extractor.get("year") or "<None>",
|
|
||||||
"value_formatters": [TextFormatter.yellow],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Special info",
|
|
||||||
"label_formatters": [TextFormatter.bold, TextFormatter.blue],
|
|
||||||
"value": self.extractor.get("special_info") or "<None>",
|
|
||||||
"value_formatters": [
|
|
||||||
SpecialInfoFormatter.format_special_info,
|
|
||||||
TextFormatter.yellow,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Source",
|
|
||||||
"label_formatters": [TextFormatter.bold, TextFormatter.blue],
|
|
||||||
"value": self.extractor.get("source") or "<None>",
|
|
||||||
"value_formatters": [TextFormatter.yellow],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Frame class",
|
|
||||||
"label_formatters": [TextFormatter.bold, TextFormatter.blue],
|
|
||||||
"value": self.extractor.get("frame_class") or "<None>",
|
|
||||||
"value_formatters": [TextFormatter.yellow],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "HDR",
|
|
||||||
"label_formatters": [TextFormatter.bold, TextFormatter.blue],
|
|
||||||
"value": self.extractor.get("hdr") or "<None>",
|
|
||||||
"value_formatters": [TextFormatter.yellow],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Audio langs",
|
|
||||||
"label_formatters": [TextFormatter.bold, TextFormatter.blue],
|
|
||||||
"value": self.extractor.get("audio_langs") or "<None>",
|
|
||||||
"value_formatters": [TextFormatter.yellow],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Database Info",
|
|
||||||
"label_formatters": [TextFormatter.bold, TextFormatter.blue],
|
|
||||||
"value": self.extractor.get("movie_db") or "<None>",
|
|
||||||
"value_formatters": [SpecialInfoFormatter.format_database_info, TextFormatter.yellow],
|
|
||||||
}
|
|
||||||
]
|
|
||||||
return FormatterApplier.format_data_items(data)
|
|
||||||
|
|||||||
456
renamer/views/media_panel_properties.py
Normal file
456
renamer/views/media_panel_properties.py
Normal file
@@ -0,0 +1,456 @@
|
|||||||
|
"""Media panel property methods using decorator pattern.
|
||||||
|
|
||||||
|
This module contains all the formatted property methods that return
|
||||||
|
display-ready values for the media panel view. Each property uses
|
||||||
|
decorators to apply formatting, similar to ProposedFilenameView.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ..formatters import (
|
||||||
|
date_decorators,
|
||||||
|
text_decorators,
|
||||||
|
conditional_decorators,
|
||||||
|
size_decorators,
|
||||||
|
extension_decorators,
|
||||||
|
duration_decorators,
|
||||||
|
resolution_decorators,
|
||||||
|
special_info_decorators,
|
||||||
|
track_decorators,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MediaPanelProperties:
|
||||||
|
"""Formatted properties for media panel display.
|
||||||
|
|
||||||
|
This class provides @property methods that return formatted values
|
||||||
|
ready for display in the media panel. Each property applies the
|
||||||
|
appropriate decorators for styling and formatting.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, extractor):
|
||||||
|
self._extractor = extractor
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# Section Title Formatter
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
@text_decorators.bold()
|
||||||
|
@text_decorators.uppercase()
|
||||||
|
def title(self, title: str) -> str:
|
||||||
|
"""Format section title with bold and uppercase styling."""
|
||||||
|
return title
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# File Info Properties
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("Path: ")
|
||||||
|
@text_decorators.blue()
|
||||||
|
@text_decorators.escape()
|
||||||
|
def file_path(self) -> str:
|
||||||
|
"""Get file path formatted with label."""
|
||||||
|
return self._extractor.get("file_path")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("Size: ")
|
||||||
|
@text_decorators.green()
|
||||||
|
@size_decorators.size_full()
|
||||||
|
def file_size(self) -> str:
|
||||||
|
"""Get file size formatted with label."""
|
||||||
|
return self._extractor.get("file_size")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("Name: ")
|
||||||
|
@text_decorators.cyan()
|
||||||
|
@text_decorators.escape()
|
||||||
|
def file_name(self) -> str:
|
||||||
|
"""Get file name formatted with label."""
|
||||||
|
return self._extractor.get("file_name")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("Modified: ")
|
||||||
|
@text_decorators.magenta()
|
||||||
|
@date_decorators.modification_date()
|
||||||
|
def modification_time(self) -> str:
|
||||||
|
"""Get modification time formatted with label."""
|
||||||
|
return self._extractor.get("modification_time")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("Extension: ")
|
||||||
|
@text_decorators.green()
|
||||||
|
@extension_decorators.extension_info()
|
||||||
|
def extension_fileinfo(self) -> str:
|
||||||
|
"""Get extension from FileInfo formatted with label."""
|
||||||
|
return self._extractor.get("extension")
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# TMDB Properties
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
@property
|
||||||
|
@text_decorators.blue()
|
||||||
|
@conditional_decorators.wrap("ID: ")
|
||||||
|
@text_decorators.yellow()
|
||||||
|
@conditional_decorators.default("<None>")
|
||||||
|
def tmdb_id(self) -> str:
|
||||||
|
"""Get TMDB ID formatted with label."""
|
||||||
|
return self._extractor.get("tmdb_id", "TMDB")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@text_decorators.blue()
|
||||||
|
@conditional_decorators.wrap("Title: ")
|
||||||
|
@text_decorators.yellow()
|
||||||
|
@conditional_decorators.default("<None>")
|
||||||
|
def tmdb_title(self) -> str:
|
||||||
|
"""Get TMDB title formatted with label."""
|
||||||
|
return self._extractor.get("title", "TMDB")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@text_decorators.blue()
|
||||||
|
@conditional_decorators.wrap("Original Title: ")
|
||||||
|
@text_decorators.yellow()
|
||||||
|
@conditional_decorators.default("<None>")
|
||||||
|
def tmdb_original_title(self) -> str:
|
||||||
|
"""Get TMDB original title formatted with label."""
|
||||||
|
return self._extractor.get("original_title", "TMDB")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@text_decorators.blue()
|
||||||
|
@conditional_decorators.wrap("Year: ")
|
||||||
|
@text_decorators.yellow()
|
||||||
|
@conditional_decorators.default("<None>")
|
||||||
|
def tmdb_year(self) -> str:
|
||||||
|
"""Get TMDB year formatted with label."""
|
||||||
|
return self._extractor.get("year", "TMDB")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@text_decorators.blue()
|
||||||
|
@conditional_decorators.wrap("Database Info: ")
|
||||||
|
@text_decorators.yellow()
|
||||||
|
@conditional_decorators.default("<None>")
|
||||||
|
@special_info_decorators.database_info()
|
||||||
|
def tmdb_database_info(self) -> str:
|
||||||
|
"""Get TMDB database info formatted with label."""
|
||||||
|
return self._extractor.get("movie_db", "TMDB")
|
||||||
|
|
||||||
|
@property
|
||||||
|
# @text_decorators.blue()
|
||||||
|
@conditional_decorators.default("")
|
||||||
|
@text_decorators.url()
|
||||||
|
def tmdb_url(self) -> str:
|
||||||
|
"""Get TMDB URL formatted with label."""
|
||||||
|
return self._extractor.get("tmdb_url", "TMDB")
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# Metadata Extraction Properties
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("Title: ")
|
||||||
|
@text_decorators.grey()
|
||||||
|
@conditional_decorators.default("Not extracted")
|
||||||
|
def metadata_title(self) -> str:
|
||||||
|
"""Get metadata title formatted with label."""
|
||||||
|
return self._extractor.get("title", "Metadata")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("Duration: ")
|
||||||
|
@text_decorators.grey()
|
||||||
|
@conditional_decorators.default("Not extracted")
|
||||||
|
@duration_decorators.duration_full()
|
||||||
|
def metadata_duration(self) -> str:
|
||||||
|
"""Get metadata duration formatted with label."""
|
||||||
|
return self._extractor.get("duration", "Metadata")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("Artist: ")
|
||||||
|
@text_decorators.grey()
|
||||||
|
@conditional_decorators.default("Not extracted")
|
||||||
|
def metadata_artist(self) -> str:
|
||||||
|
"""Get metadata artist formatted with label."""
|
||||||
|
return self._extractor.get("artist", "Metadata")
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# MediaInfo Extraction Properties
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("Duration: ")
|
||||||
|
@text_decorators.grey()
|
||||||
|
@conditional_decorators.default("Not extracted")
|
||||||
|
@duration_decorators.duration_full()
|
||||||
|
def mediainfo_duration(self) -> str:
|
||||||
|
"""Get MediaInfo duration formatted with label."""
|
||||||
|
return self._extractor.get("duration", "MediaInfo")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("Frame Class: ")
|
||||||
|
@text_decorators.grey()
|
||||||
|
@conditional_decorators.default("Not extracted")
|
||||||
|
def mediainfo_frame_class(self) -> str:
|
||||||
|
"""Get MediaInfo frame class formatted with label."""
|
||||||
|
return self._extractor.get("frame_class", "MediaInfo")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("Resolution: ")
|
||||||
|
@text_decorators.grey()
|
||||||
|
@conditional_decorators.default("Not extracted")
|
||||||
|
@resolution_decorators.resolution_dimensions()
|
||||||
|
def mediainfo_resolution(self) -> str:
|
||||||
|
"""Get MediaInfo resolution formatted with label."""
|
||||||
|
return self._extractor.get("resolution", "MediaInfo")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("Aspect Ratio: ")
|
||||||
|
@text_decorators.grey()
|
||||||
|
@conditional_decorators.default("Not extracted")
|
||||||
|
def mediainfo_aspect_ratio(self) -> str:
|
||||||
|
"""Get MediaInfo aspect ratio formatted with label."""
|
||||||
|
return self._extractor.get("aspect_ratio", "MediaInfo")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("HDR: ")
|
||||||
|
@text_decorators.grey()
|
||||||
|
@conditional_decorators.default("Not extracted")
|
||||||
|
def mediainfo_hdr(self) -> str:
|
||||||
|
"""Get MediaInfo HDR formatted with label."""
|
||||||
|
return self._extractor.get("hdr", "MediaInfo")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("Audio Languages: ")
|
||||||
|
@text_decorators.grey()
|
||||||
|
@conditional_decorators.default("Not extracted")
|
||||||
|
def mediainfo_audio_langs(self) -> str:
|
||||||
|
"""Get MediaInfo audio languages formatted with label."""
|
||||||
|
return self._extractor.get("audio_langs", "MediaInfo")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("Anamorphic: ")
|
||||||
|
@text_decorators.grey()
|
||||||
|
@conditional_decorators.default("Not extracted")
|
||||||
|
def mediainfo_anamorphic(self) -> str:
|
||||||
|
"""Get MediaInfo anamorphic formatted with label."""
|
||||||
|
return self._extractor.get("anamorphic", "MediaInfo")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("Extension: ")
|
||||||
|
@text_decorators.grey()
|
||||||
|
@conditional_decorators.default("Not extracted")
|
||||||
|
@extension_decorators.extension_info()
|
||||||
|
def mediainfo_extension(self) -> str:
|
||||||
|
"""Get MediaInfo extension formatted with label."""
|
||||||
|
return self._extractor.get("extension", "MediaInfo")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("3D Layout: ")
|
||||||
|
@text_decorators.grey()
|
||||||
|
@conditional_decorators.default("Not extracted")
|
||||||
|
def mediainfo_3d_layout(self) -> str:
|
||||||
|
"""Get MediaInfo 3D layout formatted with label."""
|
||||||
|
return self._extractor.get("3d_layout", "MediaInfo")
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# Filename Extraction Properties
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("Order: ")
|
||||||
|
@text_decorators.yellow()
|
||||||
|
@conditional_decorators.default("Not extracted")
|
||||||
|
def filename_order(self) -> str:
|
||||||
|
"""Get filename order formatted with label."""
|
||||||
|
return self._extractor.get("order", "Filename")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("Movie title: ")
|
||||||
|
@text_decorators.grey()
|
||||||
|
@conditional_decorators.default("")
|
||||||
|
def filename_title(self) -> str:
|
||||||
|
"""Get filename title formatted with label."""
|
||||||
|
return self._extractor.get("title", "Filename")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("Year: ")
|
||||||
|
@text_decorators.grey()
|
||||||
|
@conditional_decorators.default("")
|
||||||
|
def filename_year(self) -> str:
|
||||||
|
"""Get filename year formatted with label."""
|
||||||
|
return self._extractor.get("year", "Filename")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("Video source: ")
|
||||||
|
@text_decorators.grey()
|
||||||
|
@conditional_decorators.default("Not extracted")
|
||||||
|
def filename_source(self) -> str:
|
||||||
|
"""Get filename source formatted with label."""
|
||||||
|
return self._extractor.get("source", "Filename")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("Frame class: ")
|
||||||
|
@text_decorators.grey()
|
||||||
|
@conditional_decorators.default("Not extracted")
|
||||||
|
def filename_frame_class(self) -> str:
|
||||||
|
"""Get filename frame class formatted with label."""
|
||||||
|
return self._extractor.get("frame_class", "Filename")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("HDR: ")
|
||||||
|
@text_decorators.grey()
|
||||||
|
@conditional_decorators.default("Not extracted")
|
||||||
|
def filename_hdr(self) -> str:
|
||||||
|
"""Get filename HDR formatted with label."""
|
||||||
|
return self._extractor.get("hdr", "Filename")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("Audio langs: ")
|
||||||
|
@text_decorators.grey()
|
||||||
|
@conditional_decorators.default("Not extracted")
|
||||||
|
def filename_audio_langs(self) -> str:
|
||||||
|
"""Get filename audio languages formatted with label."""
|
||||||
|
return self._extractor.get("audio_langs", "Filename")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("Special info: ")
|
||||||
|
@text_decorators.grey()
|
||||||
|
@conditional_decorators.default("Not extracted")
|
||||||
|
@text_decorators.blue()
|
||||||
|
@special_info_decorators.special_info()
|
||||||
|
def filename_special_info(self) -> str:
|
||||||
|
"""Get filename special info formatted with label."""
|
||||||
|
return self._extractor.get("special_info", "Filename")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@conditional_decorators.wrap("Movie DB: ")
|
||||||
|
@text_decorators.grey()
|
||||||
|
@conditional_decorators.default("Not extracted")
|
||||||
|
def filename_movie_db(self) -> str:
|
||||||
|
"""Get filename movie DB formatted with label."""
|
||||||
|
return self._extractor.get("movie_db", "Filename")
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# Selected Data Properties
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
@property
|
||||||
|
@text_decorators.blue()
|
||||||
|
@conditional_decorators.wrap("Order: ")
|
||||||
|
@text_decorators.yellow()
|
||||||
|
@conditional_decorators.default("<None>")
|
||||||
|
def selected_order(self) -> str:
|
||||||
|
"""Get selected order formatted with label."""
|
||||||
|
return self._extractor.get("order")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@text_decorators.blue()
|
||||||
|
@conditional_decorators.wrap("Title: ")
|
||||||
|
@text_decorators.yellow()
|
||||||
|
@conditional_decorators.default("<None>")
|
||||||
|
def selected_title(self) -> str:
|
||||||
|
"""Get selected title formatted with label."""
|
||||||
|
return self._extractor.get("title")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@text_decorators.blue()
|
||||||
|
@conditional_decorators.wrap("Year: ")
|
||||||
|
@text_decorators.yellow()
|
||||||
|
@conditional_decorators.default("<None>")
|
||||||
|
def selected_year(self) -> str:
|
||||||
|
"""Get selected year formatted with label."""
|
||||||
|
return self._extractor.get("year")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@text_decorators.blue()
|
||||||
|
@conditional_decorators.wrap("Special info: ")
|
||||||
|
@text_decorators.yellow()
|
||||||
|
@conditional_decorators.default("<None>")
|
||||||
|
@special_info_decorators.special_info()
|
||||||
|
def selected_special_info(self) -> str:
|
||||||
|
"""Get selected special info formatted with label."""
|
||||||
|
return self._extractor.get("special_info")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@text_decorators.blue()
|
||||||
|
@conditional_decorators.wrap("Source: ")
|
||||||
|
@text_decorators.yellow()
|
||||||
|
@conditional_decorators.default("<None>")
|
||||||
|
def selected_source(self) -> str:
|
||||||
|
"""Get selected source formatted with label."""
|
||||||
|
return self._extractor.get("source")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@text_decorators.blue()
|
||||||
|
@conditional_decorators.wrap("Frame class: ")
|
||||||
|
@text_decorators.yellow()
|
||||||
|
@conditional_decorators.default("<None>")
|
||||||
|
def selected_frame_class(self) -> str:
|
||||||
|
"""Get selected frame class formatted with label."""
|
||||||
|
return self._extractor.get("frame_class")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@text_decorators.blue()
|
||||||
|
@conditional_decorators.wrap("HDR: ")
|
||||||
|
@text_decorators.yellow()
|
||||||
|
@conditional_decorators.default("<None>")
|
||||||
|
def selected_hdr(self) -> str:
|
||||||
|
"""Get selected HDR formatted with label."""
|
||||||
|
return self._extractor.get("hdr")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@text_decorators.blue()
|
||||||
|
@conditional_decorators.wrap("Audio langs: ")
|
||||||
|
@text_decorators.yellow()
|
||||||
|
@conditional_decorators.default("<None>")
|
||||||
|
def selected_audio_langs(self) -> str:
|
||||||
|
"""Get selected audio languages formatted with label."""
|
||||||
|
return self._extractor.get("audio_langs")
|
||||||
|
|
||||||
|
@property
|
||||||
|
@text_decorators.blue()
|
||||||
|
@conditional_decorators.wrap("Database Info: ")
|
||||||
|
@text_decorators.yellow()
|
||||||
|
@conditional_decorators.default("<None>")
|
||||||
|
@special_info_decorators.database_info()
|
||||||
|
def selected_database_info(self) -> str:
|
||||||
|
"""Get selected database info formatted with label."""
|
||||||
|
return self._extractor.get("movie_db")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def video_tracks(self) -> list[str]:
|
||||||
|
"""Return formatted video track data"""
|
||||||
|
tracks = self._extractor.get("video_tracks", "MediaInfo") or []
|
||||||
|
return [self.video_track(track, i) for i, track in enumerate(tracks, start=1)]
|
||||||
|
|
||||||
|
@text_decorators.green()
|
||||||
|
@conditional_decorators.wrap("Video Track {index}: ")
|
||||||
|
@track_decorators.video_track()
|
||||||
|
def video_track(self, track, index) -> str:
|
||||||
|
"""Get video track info formatted with label."""
|
||||||
|
return track
|
||||||
|
|
||||||
|
@property
|
||||||
|
def audio_tracks(self) -> list[str]:
|
||||||
|
"""Return formatted audio track data"""
|
||||||
|
tracks = self._extractor.get("audio_tracks", "MediaInfo") or []
|
||||||
|
return [self.audio_track(track, i) for i, track in enumerate(tracks, start=1)]
|
||||||
|
|
||||||
|
@text_decorators.yellow()
|
||||||
|
@conditional_decorators.wrap("Audio Track {index}: ")
|
||||||
|
@track_decorators.audio_track()
|
||||||
|
def audio_track(self, track, index) -> str:
|
||||||
|
"""Get audio track info formatted with label."""
|
||||||
|
return track
|
||||||
|
|
||||||
|
@property
|
||||||
|
def subtitle_tracks(self) -> list[str]:
|
||||||
|
"""Return formatted subtitle track data"""
|
||||||
|
tracks = self._extractor.get("subtitle_tracks", "MediaInfo") or []
|
||||||
|
return [
|
||||||
|
self.subtitle_track(track, i) for i, track in enumerate(tracks, start=1)
|
||||||
|
]
|
||||||
|
|
||||||
|
@text_decorators.magenta()
|
||||||
|
@conditional_decorators.wrap("Subtitle Track {index}: ")
|
||||||
|
@track_decorators.subtitle_track()
|
||||||
|
def subtitle_track(self, track, index) -> str:
|
||||||
|
"""Get subtitle track info formatted with label."""
|
||||||
|
return track
|
||||||
Reference in New Issue
Block a user