feat: Update version to 0.2.4, enhance logging capabilities, and improve special info formatting

This commit is contained in:
sha
2025-12-27 01:31:21 +00:00
parent 44a6a978b8
commit 95cbaee7fa
11 changed files with 179 additions and 78 deletions
+132
View File
@@ -0,0 +1,132 @@
from .text_formatter import TextFormatter
from .duration_formatter import DurationFormatter
from .size_formatter import SizeFormatter
from .date_formatter import DateFormatter
from .extension_formatter import ExtensionFormatter
from .resolution_formatter import ResolutionFormatter
from .track_formatter import TrackFormatter
from .special_info_formatter import SpecialInfoFormatter
import logging
import inspect
import os
# Set up logging conditionally
if os.getenv('FORMATTER_LOG', '0') == '1':
logging.basicConfig(filename='formatter.log', level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')
else:
logging.basicConfig(level=logging.CRITICAL) # Disable logging
class FormatterApplier:
"""Class to apply multiple formatters in correct order"""
# Define the global order of all formatters
FORMATTER_ORDER = [
# Data formatters first (transform raw data)
DurationFormatter.format_seconds,
DurationFormatter.format_hhmmss,
DurationFormatter.format_hhmm,
DurationFormatter.format_full,
SizeFormatter.format_size,
SizeFormatter.format_size_full,
DateFormatter.format_modification_date,
DateFormatter.format_year,
ExtensionFormatter.format_extension_info,
ResolutionFormatter.get_frame_class_from_resolution,
ResolutionFormatter.format_resolution_p,
ResolutionFormatter.format_resolution_dimensions,
TrackFormatter.format_video_track,
TrackFormatter.format_audio_track,
TrackFormatter.format_subtitle_track,
SpecialInfoFormatter.format_special_info,
# Text formatters second (transform text content)
TextFormatter.uppercase,
TextFormatter.lowercase,
TextFormatter.camelcase,
# Markup formatters last (add visual styling)
TextFormatter.bold,
TextFormatter.italic,
TextFormatter.underline,
TextFormatter.bold_green,
TextFormatter.bold_cyan,
TextFormatter.bold_magenta,
TextFormatter.bold_yellow,
TextFormatter.green,
TextFormatter.yellow,
TextFormatter.magenta,
TextFormatter.cyan,
TextFormatter.red,
TextFormatter.blue,
TextFormatter.grey,
TextFormatter.dim,
]
@staticmethod
def apply_formatters(value, formatters):
"""Apply multiple formatters to value in the global order"""
if not isinstance(formatters, list):
formatters = [formatters] if formatters else []
# Sort formatters according to the global order
ordered_formatters = sorted(formatters, key=lambda f: FormatterApplier.FORMATTER_ORDER.index(f) if f in FormatterApplier.FORMATTER_ORDER else len(FormatterApplier.FORMATTER_ORDER))
# Get caller info
frame = inspect.currentframe()
if frame and frame.f_back:
caller = f"{frame.f_back.f_code.co_filename}:{frame.f_back.f_lineno} in {frame.f_back.f_code.co_name}"
else:
caller = "Unknown"
logging.info(f"Caller: {caller}")
logging.info(f"Original formatters: {[f.__name__ if hasattr(f, '__name__') else str(f) for f in formatters]}")
logging.info(f"Ordered formatters: {[f.__name__ if hasattr(f, '__name__') else str(f) for f in ordered_formatters]}")
logging.info(f"Input value: {repr(value)}")
# Apply in the ordered sequence
for formatter in ordered_formatters:
try:
old_value = value
value = formatter(value)
logging.debug(f"Applied {formatter.__name__ if hasattr(formatter, '__name__') else str(formatter)}: {repr(old_value)} -> {repr(value)}")
except Exception as e:
logging.error(f"Error applying {formatter.__name__ if hasattr(formatter, '__name__') else str(formatter)}: {e}")
value = "Unknown"
logging.info(f"Final value: {repr(value)}")
return value
@staticmethod
def format_data_item(item: dict) -> str | None:
"""Apply all formatting to a data item and return the formatted string"""
# Handle value formatting first (e.g., size formatting)
value = item.get("value")
if value is not None and value != "Not extracted":
value_formatters = item.get("value_formatters", [])
value = FormatterApplier.apply_formatters(value, value_formatters)
# Handle label formatting
label = item.get("label", "")
if label:
label_formatters = item.get("label_formatters", [])
label = FormatterApplier.apply_formatters(label, label_formatters)
# Create the display string
if value is not None:
display_string = f"{label}: {value}"
else:
display_string = label
# Handle display formatting (e.g., color)
display_formatters = item.get("display_formatters", [])
display_string = FormatterApplier.apply_formatters(display_string, display_formatters)
return display_string
@staticmethod
def format_data_items(data: list[dict]) -> list:
"""Apply formatting to a list of data items"""
return [FormatterApplier.format_data_item(item) for item in data]
+13 -70
View File
@@ -8,6 +8,7 @@ from .track_formatter import TrackFormatter
from .resolution_formatter import ResolutionFormatter
from .duration_formatter import DurationFormatter
from .special_info_formatter import SpecialInfoFormatter
from .formatter import FormatterApplier
class MediaFormatter:
@@ -16,56 +17,6 @@ class MediaFormatter:
def __init__(self, extractor):
self.extractor = extractor
def _format_data_item(self, item: dict) -> str:
"""Apply all formatting to a data item and return the formatted string"""
# Define text formatters that should be applied before markup
text_formatters_set = {
TextFormatter.uppercase,
TextFormatter.lowercase,
TextFormatter.camelcase,
}
# Handle value formatting first (e.g., size formatting)
value = item.get("value")
if value is not None and not isinstance(value, str):
value_formatters = item.get("value_formatters", [])
if not isinstance(value_formatters, list):
value_formatters = [value_formatters] if value_formatters else []
for formatter in value_formatters:
value = formatter(value)
# Handle label formatting
label = item.get("label", "")
if label:
label_formatters = item.get("label_formatters", [])
if not isinstance(label_formatters, list):
label_formatters = [label_formatters] if label_formatters else []
# Separate text and markup formatters, apply text first
text_fs = [f for f in label_formatters if f in text_formatters_set]
markup_fs = [f for f in label_formatters if f not in text_formatters_set]
ordered_formatters = text_fs + markup_fs
for formatter in ordered_formatters:
label = formatter(label)
# Create the display string
if value is not None:
display_string = f"{label}: {value}"
else:
display_string = label
# Handle display formatting (e.g., color)
display_formatters = item.get("display_formatters", [])
if not isinstance(display_formatters, list):
display_formatters = [display_formatters] if display_formatters else []
# Separate text and markup formatters, apply text first
text_fs = [f for f in display_formatters if f in text_formatters_set]
markup_fs = [f for f in display_formatters if f not in text_formatters_set]
ordered_formatters = text_fs + markup_fs
for formatter in ordered_formatters:
display_string = formatter(display_string)
return display_string
def file_info_panel(self) -> str:
"""Return formatted file info panel string"""
sections = [
@@ -123,7 +74,7 @@ class MediaFormatter:
"display_formatters": [TextFormatter.green],
},
]
return [self._format_data_item(item) for item in data]
return FormatterApplier.format_data_items(data)
def tracks_info(self) -> list[str]:
"""Return formatted tracks information"""
@@ -174,7 +125,7 @@ class MediaFormatter:
}
)
return [self._format_data_item(item) for item in data]
return FormatterApplier.format_data_items(data)
def metadata_extracted_data(self) -> list[str]:
"""Format metadata extraction data for the metadata panel"""
@@ -204,7 +155,7 @@ class MediaFormatter:
},
]
return [self._format_data_item(item) for item in data]
return FormatterApplier.format_data_items(data)
def mediainfo_extracted_data(self) -> list[str]:
"""Format media info extraction data for the mediainfo panel"""
@@ -256,7 +207,7 @@ class MediaFormatter:
"display_formatters": [TextFormatter.grey],
},
]
return [self._format_data_item(item) for item in data]
return FormatterApplier.format_data_items(data)
def filename_extracted_data(self) -> list[str]:
"""Return formatted filename extracted data"""
@@ -328,7 +279,7 @@ class MediaFormatter:
},
]
return [self._format_data_item(item) for item in data]
return FormatterApplier.format_data_items(data)
def selected_data(self) -> list[str]:
"""Return formatted selected data string"""
@@ -337,6 +288,12 @@ class MediaFormatter:
"label": "Selected Data",
"label_formatters": [TextFormatter.bold, TextFormatter.uppercase],
},
{
"label": "Title",
"label_formatters": [TextFormatter.bold, TextFormatter.yellow],
"value": self.extractor.get("title") or "<None>",
"value_formatters": [TextFormatter.blue],
},
{
"label": "Special info",
"label_formatters": [TextFormatter.bold],
@@ -348,18 +305,4 @@ class MediaFormatter:
"display_formatters": [TextFormatter.yellow],
},
]
return [self._format_data_item(item) for item in data]
def _format_extra_metadata(self, metadata: dict) -> str:
"""Format extra metadata like duration, title, artist"""
data = {}
if metadata.get("duration"):
data["Duration"] = f"{metadata['duration']:.1f} seconds"
if metadata.get("title"):
data["Title"] = metadata["title"]
if metadata.get("artist"):
data["Artist"] = metadata["artist"]
return "\n".join(
TextFormatter.cyan(f"{key}: {value}") for key, value in data.items()
)
return FormatterApplier.format_data_items(data)
+3 -1
View File
@@ -5,5 +5,7 @@ class SpecialInfoFormatter:
def format_special_info(special_info):
"""Convert special info list to comma-separated string"""
if isinstance(special_info, list):
return ", ".join(special_info)
# Filter out None values and ensure all items are strings
filtered = [str(item) for item in special_info if item is not None]
return ", ".join(filtered)
return special_info or ""