diff --git a/dist/renamer-0.6.2-py3-none-any.whl b/dist/renamer-0.6.2-py3-none-any.whl new file mode 100644 index 0000000..a15304d Binary files /dev/null and b/dist/renamer-0.6.2-py3-none-any.whl differ diff --git a/pyproject.toml b/pyproject.toml index 2e50457..47741a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "renamer" -version = "0.6.1" +version = "0.6.2" description = "Terminal-based media file renamer and metadata viewer" readme = "README.md" requires-python = ">=3.11" diff --git a/renamer/__init__.py b/renamer/__init__.py index 741abc4..6a94b81 100644 --- a/renamer/__init__.py +++ b/renamer/__init__.py @@ -2,6 +2,6 @@ from .app import RenamerApp from .extractors.extractor import MediaExtractor -from .formatters.media_formatter import MediaFormatter +from .views import MediaPanelView, ProposedFilenameView -__all__ = ['RenamerApp', 'MediaExtractor', 'MediaFormatter'] \ No newline at end of file +__all__ = ['RenamerApp', 'MediaExtractor', 'MediaPanelView', 'ProposedFilenameView'] \ No newline at end of file diff --git a/renamer/app.py b/renamer/app.py index f13bf9d..e13a9fd 100644 --- a/renamer/app.py +++ b/renamer/app.py @@ -14,8 +14,7 @@ import os from .constants import MEDIA_TYPES from .screens import OpenScreen, HelpScreen, RenameConfirmScreen, SettingsScreen from .extractors.extractor import MediaExtractor -from .formatters.media_formatter import MediaFormatter -from .formatters.proposed_name_formatter import ProposedNameFormatter +from .views import MediaPanelView, ProposedFilenameView from .formatters.text_formatter import TextFormatter from .formatters.catalog_formatter import CatalogFormatter from .settings import Settings @@ -216,7 +215,7 @@ class RenamerApp(App): mode = self.settings.get("mode") if mode == "technical": - formatter = MediaFormatter(extractor) + formatter = MediaPanelView(extractor) full_info = formatter.file_info_panel() else: # catalog formatter = CatalogFormatter(extractor) @@ -226,7 +225,7 @@ class RenamerApp(App): self.call_later( self._update_details, full_info, - ProposedNameFormatter(extractor).rename_line_formatted(file_path), + ProposedFilenameView(extractor).rename_line_formatted(file_path), ) except Exception as e: self.call_later( @@ -345,7 +344,7 @@ By Category:""" if node and node.data and isinstance(node.data, Path) and node.data.is_file(): # Get the proposed name from the extractor extractor = MediaExtractor(node.data) - proposed_formatter = ProposedNameFormatter(extractor) + proposed_formatter = ProposedFilenameView(extractor) new_name = str(proposed_formatter) logging.info(f"Proposed new name: {new_name!r} for file: {node.data}") if new_name and new_name != node.data.name: diff --git a/renamer/services/metadata_service.py b/renamer/services/metadata_service.py index 72a5be5..e4587a7 100644 --- a/renamer/services/metadata_service.py +++ b/renamer/services/metadata_service.py @@ -16,9 +16,8 @@ from threading import Lock from renamer.cache import Cache from renamer.settings import Settings from renamer.extractors.extractor import MediaExtractor -from renamer.formatters.media_formatter import MediaFormatter +from renamer.views import MediaPanelView, ProposedFilenameView from renamer.formatters.catalog_formatter import CatalogFormatter -from renamer.formatters.proposed_name_formatter import ProposedNameFormatter from renamer.formatters.text_formatter import TextFormatter @@ -171,14 +170,14 @@ class MetadataService: # Format based on mode if mode == "technical": - formatter = MediaFormatter(extractor) + formatter = MediaPanelView(extractor) formatted_info = formatter.file_info_panel() else: # catalog formatter = CatalogFormatter(extractor) formatted_info = formatter.format_catalog_info() # Generate proposed name - proposed_formatter = ProposedNameFormatter(extractor) + proposed_formatter = ProposedFilenameView(extractor) proposed_name = proposed_formatter.rename_line_formatted(file_path) return { diff --git a/renamer/services/rename_service.py b/renamer/services/rename_service.py index 3bc1e31..1d5bd6d 100644 --- a/renamer/services/rename_service.py +++ b/renamer/services/rename_service.py @@ -14,7 +14,7 @@ from pathlib import Path from typing import Optional, Callable from renamer.extractors.extractor import MediaExtractor -from renamer.formatters.proposed_name_formatter import ProposedNameFormatter +from renamer.views import ProposedFilenameView logger = logging.getLogger(__name__) @@ -80,7 +80,7 @@ class RenameService: if extractor is None: extractor = MediaExtractor(file_path) - formatter = ProposedNameFormatter(extractor) + formatter = ProposedFilenameView(extractor) # Get the formatted rename line rename_line = formatter.rename_line_formatted(file_path) diff --git a/renamer/test/test_proposed_name_formatter.py b/renamer/test/test_proposed_filename_view.py similarity index 85% rename from renamer/test/test_proposed_name_formatter.py rename to renamer/test/test_proposed_filename_view.py index 911f666..a1fcd7c 100644 --- a/renamer/test/test_proposed_name_formatter.py +++ b/renamer/test/test_proposed_filename_view.py @@ -1,12 +1,12 @@ -"""Tests for ProposedNameFormatter with decorator pattern.""" +"""Tests for ProposedFilenameView with decorator pattern.""" import pytest from pathlib import Path -from renamer.formatters.proposed_name_formatter import ProposedNameFormatter +from renamer.views import ProposedFilenameView -class TestProposedNameFormatter: - """Test ProposedNameFormatter with decorator pattern.""" +class TestProposedFilenameView: + """Test ProposedFilenameView with decorator pattern.""" def test_basic_formatting(self): """Test basic filename formatting with all fields.""" @@ -23,7 +23,7 @@ class TestProposedNameFormatter: 'extension': 'mkv' } - formatter = ProposedNameFormatter(extractor) + formatter = ProposedFilenameView(extractor) result = formatter.rename_line assert '[01]' in result @@ -45,7 +45,7 @@ class TestProposedNameFormatter: 'extension': 'mp4' } - formatter = ProposedNameFormatter(extractor) + formatter = ProposedFilenameView(extractor) result = formatter.rename_line assert 'Simple Movie' in result @@ -61,7 +61,7 @@ class TestProposedNameFormatter: 'extension': 'mkv' } - formatter = ProposedNameFormatter(extractor) + formatter = ProposedFilenameView(extractor) result = formatter.rename_line assert 'Movie-Title-Test' in result @@ -76,7 +76,7 @@ class TestProposedNameFormatter: 'extension': 'mkv' } - formatter = ProposedNameFormatter(extractor) + formatter = ProposedFilenameView(extractor) result = formatter.rename_line # Since title is None, it won't appear (unless extractor provides default) @@ -90,7 +90,7 @@ class TestProposedNameFormatter: 'extension': None } - formatter = ProposedNameFormatter(extractor) + formatter = ProposedFilenameView(extractor) result = formatter.rename_line # Extension handling depends on extractor default @@ -105,7 +105,7 @@ class TestProposedNameFormatter: 'extension': 'mkv' } - formatter = ProposedNameFormatter(extractor) + formatter = ProposedFilenameView(extractor) result = formatter.rename_line assert 'Extended Edition, Remastered' in result @@ -119,7 +119,7 @@ class TestProposedNameFormatter: 'extension': 'mkv' } - formatter = ProposedNameFormatter(extractor) + formatter = ProposedFilenameView(extractor) result = formatter.rename_line assert 'imdbid-tt1234567' in result @@ -132,7 +132,7 @@ class TestProposedNameFormatter: 'extension': 'mkv' } - formatter = ProposedNameFormatter(extractor) + formatter = ProposedFilenameView(extractor) assert str(formatter) == formatter.rename_line def test_formatted_display_matching_name(self): @@ -143,7 +143,7 @@ class TestProposedNameFormatter: 'extension': 'mkv' } - formatter = ProposedNameFormatter(extractor) + formatter = ProposedFilenameView(extractor) proposed = str(formatter) file_path = Path(proposed) @@ -160,7 +160,7 @@ class TestProposedNameFormatter: 'extension': 'mkv' } - formatter = ProposedNameFormatter(extractor) + formatter = ProposedFilenameView(extractor) file_path = Path('different_name.mkv') result = formatter.rename_line_formatted(file_path) @@ -175,7 +175,7 @@ class TestProposedNameFormatter: 'extension': 'mkv' } - formatter = ProposedNameFormatter(extractor) + formatter = ProposedFilenameView(extractor) result = formatter.rename_line assert '(2020)' in result @@ -188,7 +188,7 @@ class TestProposedNameFormatter: 'extension': 'mkv' } - formatter = ProposedNameFormatter(extractor) + formatter = ProposedFilenameView(extractor) result = formatter.rename_line # Should not have empty parentheses diff --git a/renamer/views/__init__.py b/renamer/views/__init__.py new file mode 100644 index 0000000..01e8157 --- /dev/null +++ b/renamer/views/__init__.py @@ -0,0 +1,14 @@ +"""Views package - assembles formatted data for display. + +Views compose multiple formatters to create complex display outputs. +Unlike formatters which transform single values, views aggregate and +orchestrate multiple formatters to build complete UI panels. +""" + +from .proposed_filename import ProposedFilenameView +from .media_panel import MediaPanelView + +__all__ = [ + 'ProposedFilenameView', + 'MediaPanelView', +] diff --git a/renamer/formatters/media_formatter.py b/renamer/views/media_panel.py similarity index 95% rename from renamer/formatters/media_formatter.py rename to renamer/views/media_panel.py index ac82445..e3ccc30 100644 --- a/renamer/formatters/media_formatter.py +++ b/renamer/views/media_panel.py @@ -1,18 +1,22 @@ from pathlib import Path from rich.markup import escape -from .size_formatter import SizeFormatter -from .date_formatter import DateFormatter -from .extension_formatter import ExtensionFormatter -from .text_formatter import TextFormatter -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 +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 MediaFormatter: - """Class to format media data for display""" +class MediaPanelView: + """View for assembling media data panels for display. + + This view aggregates multiple formatters to create comprehensive + display panels for technical and catalog modes. + """ def __init__(self, extractor): self.extractor = extractor diff --git a/renamer/formatters/proposed_name_formatter.py b/renamer/views/proposed_filename.py similarity index 83% rename from renamer/formatters/proposed_name_formatter.py rename to renamer/views/proposed_filename.py index 6b98e05..912bac1 100644 --- a/renamer/formatters/proposed_name_formatter.py +++ b/renamer/views/proposed_filename.py @@ -1,11 +1,15 @@ from rich.markup import escape -from .special_info_decorators import special_info_decorators -from .conditional_decorators import conditional_decorators -from .text_decorators import text_decorators +from ..formatters.special_info_decorators import special_info_decorators +from ..formatters.conditional_decorators import conditional_decorators +from ..formatters.text_decorators import text_decorators -class ProposedNameFormatter: - """Class for formatting proposed filenames using decorator pattern with properties.""" +class ProposedFilenameView: + """View for generating proposed filenames using decorator pattern with properties. + + This view composes formatter decorators to generate clean, standardized filenames + from extracted metadata. It uses property decorators for declarative formatting. + """ def __init__(self, extractor): """Initialize with media extractor data""" @@ -87,12 +91,14 @@ class ProposedNameFormatter: return self.rename_line_different @property + @conditional_decorators.wrap(">> ", " <<") @text_decorators.green() def rename_line_similar(self) -> str: """Generate a simplified proposed filename for similarity checks.""" return escape(str(self)) - + @property + @conditional_decorators.wrap(">> ", " <<") @text_decorators.orange() def rename_line_different(self) -> str: """Generate a detailed proposed filename for difference checks.""" diff --git a/uv.lock b/uv.lock index 6717c84..425ed01 100644 --- a/uv.lock +++ b/uv.lock @@ -462,7 +462,7 @@ wheels = [ [[package]] name = "renamer" -version = "0.6.1" +version = "0.6.2" source = { editable = "." } dependencies = [ { name = "langcodes" },