diff --git a/.gitignore b/.gitignore index 6fb729e..c0db26b 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ wheels/ *.log # Virtual environments .venv +venv/ # Test-generated files src/test/datasets/sample_mediafiles/ .pytest_cache/ diff --git a/AGENTS.md b/AGENTS.md index c635c00..405edf3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -68,7 +68,7 @@ moma is a sophisticated Terminal User Interface (TUI) application for managing, ``` ┌─────────────────────────────────────────┐ │ TUI Layer (Textual) │ -│ app.py, screens.py │ +│ app.py, views/ │ └─────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────┐ @@ -241,8 +241,8 @@ year = extractor.get("year", source="Filename") # Force specific source - `MarkupFormatter`: For visual styling (colors, bold, links) - `CompositeFormatter`: For chaining multiple formatters -#### FormatterApplier (`formatter.py`) -**Coordinator** ensuring correct formatter order: +#### FormatterApplier +Formatters are applied via decorator modules (`*_decorators.py`) using the base classes in `base.py`. **Order**: Data → Text → Markup @@ -251,25 +251,17 @@ year = extractor.get("year", source="Filename") # Force specific source 2. Text formatters (uppercase, lowercase, camelcase) 3. Markup formatters (bold, colors, dim, underline) -**Usage**: -```python -formatters = [SizeFormatter.format_size, TextFormatter.bold] -result = FormatterApplier.apply_formatters(1024, formatters) -# Result: bold("1.00 KB") -``` - #### Specialized Formatters -- **MediaFormatter**: Main coordinator, mode-aware (technical/catalog) -- **CatalogFormatter**: TMDB data, ratings, genres, poster display -- **TrackFormatter**: Video/audio/subtitle track formatting with colors -- **ProposedNameFormatter**: Intelligent rename suggestions -- **SizeFormatter**: Human-readable file sizes -- **DurationFormatter**: Duration in HH:MM:SS -- **DateFormatter**: Timestamp formatting -- **ResolutionFormatter**: Resolution display -- **ExtensionFormatter**: File extension handling -- **SpecialInfoFormatter**: Edition/source formatting -- **TextFormatter**: Text styling utilities +- **CatalogFormatter** (`catalog_formatter.py`): TMDB data, ratings, genres, poster display +- **TrackFormatter** (`track_formatter.py`): Video/audio/subtitle track formatting with colors +- **SizeFormatter** (`size_formatter.py`): Human-readable file sizes +- **DurationFormatter** (`duration_formatter.py`): Duration in HH:MM:SS +- **DateFormatter** (`date_formatter.py`): Timestamp formatting +- **ResolutionFormatter** (`resolution_formatter.py`): Resolution display +- **ExtensionFormatter** (`extension_formatter.py`): File extension handling +- **SpecialInfoFormatter** (`special_info_formatter.py`): Edition/source formatting +- **TextFormatter** (`text_formatter.py`): Text styling utilities +- **Decorator modules** (`*_decorators.py`): Reusable display decorators per formatter type ### 5. Utility Modules (`src/utils/`) @@ -391,7 +383,7 @@ Access via Ctrl+P: 1. **OpenScreen** (`open_screen.py`): Directory selection dialog with validation 2. **HelpScreen** (`help_screen.py`): Comprehensive help with key bindings 3. **RenameConfirmScreen** (`rename_confirm_screen.py`): File rename confirmation with error handling -4. **ConvertConfirmScreen** (`convert_confirm_screen.py`): AVI/MP4/WebM → MKV conversion confirmation +4. **ConvertConfirmScreen** (`convert_confirm_screen.py`): AVI/MPG/MPEG/MP4/WebM → MKV conversion confirmation 5. **DeleteConfirmScreen** (`delete_confirm_screen.py`): File deletion confirmation 6. **SettingsScreen** (`settings_screen.py`): Settings configuration interface @@ -402,10 +394,13 @@ Access via Ctrl+P: **Options**: ```json { - "mode": "technical", // or "catalog" - "cache_ttl_extractors": 21600, // 6 hours - "cache_ttl_tmdb": 21600, // 6 hours - "cache_ttl_posters": 2592000 // 30 days + "mode": "technical", // "technical" or "catalog" + "poster": "no", // "no", "pseudo" (ASCII art), "viu", "richpixels" + "hevc_crf": 23, // HEVC quality: 18=lossless, 23=high, 28=balanced + "hevc_preset": "fast", // HEVC speed: ultrafast, veryfast, faster, fast, medium, slow + "cache_ttl_extractors": 21600, // 6 hours in seconds + "cache_ttl_tmdb": 21600, // 6 hours in seconds + "cache_ttl_posters": 2592000 // 30 days in seconds } ``` @@ -779,8 +774,8 @@ uv run release # Bump + sync + build ``` dist/ -├── moma-0.7.0-py3-none-any.whl # Wheel distribution -└── moma-0.7.0.tar.gz # Source distribution +├── moma-0.8.11-py3-none-any.whl # Wheel distribution +└── moma-0.8.11.tar.gz # Source distribution ``` --- @@ -852,13 +847,16 @@ Title (Year) [Resolution Source Edition].ext |-----|--------| | `q` | Quit application | | `o` | Open directory | -| `s` | Scan/rescan directory | +| `s` | Scan current node's directory | +| `Ctrl+S` | Scan entire directory tree | | `f` | Refresh metadata for selected file | | `r` | Rename file with proposed name | -| `p` | Toggle tree expansion | +| `c` | Convert to MKV | +| `d` | Delete selected file | +| `t` | Toggle tree expansion | | `m` | Toggle mode (technical/catalog) | +| `p` | Settings | | `h` | Show help screen | -| `Ctrl+S` | Open settings | | `Ctrl+P` | Open command palette | --- @@ -884,7 +882,7 @@ Title (Year) [Resolution Source Edition].ext ## Security Considerations -- Input sanitization for filenames (see `ProposedNameFormatter`) +- Input sanitization for filenames (see `ProposedFilenameView`) - No shell command injection risks - Safe file operations (pathlib, proper error handling) - TMDB API key should not be committed (stored in `secrets.py`) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fc7824..01280dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -117,7 +117,7 @@ Significant refactoring focused on code quality, architecture, and maintainabili - `LanguageCodeExtractor`: Language code processing - `FrameClassMatcher`: Resolution/frame class matching - **Command Palette Integration**: - - `AppCommandProvider`: 8 main app commands + - `AppCommandProvider`: 11 main app commands (open, scan_local, scan, refresh, rename, convert, delete, toggle_mode, expand, settings, help) - `CacheCommandProvider`: 7 cache management commands - Access via Ctrl+P diff --git a/README.md b/README.md index e8b63f9..3b715a4 100644 --- a/README.md +++ b/README.md @@ -52,13 +52,16 @@ moma /path/to/media |-----|--------| | `q` | Quit | | `o` | Open directory | -| `s` | Scan/rescan | +| `s` | Scan current node | +| `Ctrl+S` | Scan entire tree | | `f` | Refresh metadata | | `r` | Rename file | +| `c` | Convert to MKV | +| `d` | Delete file | +| `t` | Toggle tree expansion | | `m` | Toggle mode (technical/catalog) | -| `p` | Toggle tree expansion | +| `p` | Settings | | `h` | Show help | -| `Ctrl+S` | Settings | | `Ctrl+P` | Command palette | --- @@ -101,13 +104,16 @@ Toggle with `m` key. ```json { "mode": "technical", + "poster": "no", + "hevc_crf": 23, + "hevc_preset": "fast", "cache_ttl_extractors": 21600, "cache_ttl_tmdb": 21600, "cache_ttl_posters": 2592000 } ``` -Access via `Ctrl+S` or edit file directly. +Access via `p` (Settings screen) or edit file directly. --- diff --git a/docs/REFACTORING_PROGRESS.md b/docs/REFACTORING_PROGRESS.md index 126aea0..b3ad965 100644 --- a/docs/REFACTORING_PROGRESS.md +++ b/docs/REFACTORING_PROGRESS.md @@ -19,94 +19,17 @@ This document tracks the future refactoring plan for moma v0.8.x+. ## Pending Phases -### Phase 3.6: Cleanup and Preparation (0/2) - -**Goal**: Clean up remaining issues before major refactoring. - -**Status**: NOT STARTED -**Priority**: HIGH (Must complete before Phase 4) +### Phase 3.6: Cleanup and Preparation (2/2 COMPLETED) #### 3.6.1 Refactor ProposedNameFormatter to Use Decorator Pattern -**Status**: NOT STARTED +**Status**: ✅ COMPLETED -**Current Issue**: `ProposedNameFormatter` stores extracted values in `__init__` as instance variables, creating unnecessary coupling. - -**Goal**: Convert to functional/decorator pattern similar to other formatters. - -**Current Code**: -```python -class ProposedNameFormatter: - def __init__(self, extractor): - self.__order = extractor.get('order') - self.__title = extractor.get('title') - # ... more instance variables - - def rename_line(self) -> str: - return f"{self.__order}{self.__title}..." -``` - -**Target Design**: -```python -class ProposedNameFormatter: - @staticmethod - def format_proposed_name(extractor) -> str: - """Generate proposed filename from extractor data""" - # Direct formatting without storing state - order = format_order(extractor.get('order')) - title = format_title(extractor.get('title')) - return f"{order}{title}..." - - @staticmethod - def format_proposed_name_with_color(file_path, extractor) -> str: - """Format proposed name with color highlighting""" - proposed = ProposedNameFormatter.format_proposed_name(extractor) - # Color logic here -``` - -**Benefits**: -- Stateless, pure functions -- Easier to test -- Consistent with other formatters -- Can use `@cached()` decorator if needed -- No coupling to extractor instance - -**Files to Modify**: -- `src/formatters/proposed_name_formatter.py` -- Update all usages in `app.py`, `screens.py`, etc. - ---- +The class was renamed `ProposedFilenameView` and moved to `src/views/proposed_filename.py`. It now uses the decorator pattern with `@conditional_decorators`, `@text_decorators`, etc. — consistent with the rest of the formatter system. #### 3.6.2 Clean Up Decorators Directory -**Status**: NOT STARTED +**Status**: ✅ COMPLETED -**Current Issue**: `src/decorators/` directory contains legacy `caching.py` file that's no longer used. All cache decorators were moved to `src/cache/decorators.py` in Phase 1. - -**Current Structure**: -``` -src/decorators/ -├── caching.py # ⚠️ LEGACY - Remove -└── __init__.py # Import from src.cache -``` - -**Actions**: -1. **Verify no direct imports** of `src.decorators.caching` -2. **Remove `caching.py`** - All functionality now in `src/cache/decorators.py` -3. **Keep `__init__.py`** for backward compatibility (imports from `src.cache`) -4. **Update any direct imports** to use `from src.cache import cached_method` - -**Verification**: -```bash -# Check for direct imports of old caching module -grep -r "from src.decorators.caching" src/ -grep -r "import src.decorators.caching" src/ - -# Should only find imports from __init__.py that re-export from src.cache -``` - -**Benefits**: -- Removes dead code -- Clarifies that all caching is in `src/cache/` -- Maintains backward compatibility via `__init__.py` +`src/decorators/` directory no longer exists. All cache decorators live in `src/cache/decorators.py`. ---