- Deleted the `renamer.decorators` package, including `caching.py` and `__init__.py`, to streamline the codebase. - Updated tests to reflect changes in import paths for caching decorators. - Added a comprehensive changelog to document major refactoring efforts and future plans. - Introduced an engineering guide detailing architecture, core components, and development setup.
945 lines
27 KiB
Markdown
945 lines
27 KiB
Markdown
# Renamer Engineering Guide
|
|
|
|
**Version**: 0.7.0-dev
|
|
**Last Updated**: 2026-01-01
|
|
**Python**: 3.11+
|
|
**Status**: Active Development
|
|
|
|
This is the comprehensive technical reference for the Renamer project. It contains all architectural information, implementation details, development workflows, and AI assistant instructions.
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
|
|
1. [Project Overview](#project-overview)
|
|
2. [Architecture](#architecture)
|
|
3. [Core Components](#core-components)
|
|
4. [Development Setup](#development-setup)
|
|
5. [Testing Strategy](#testing-strategy)
|
|
6. [Code Standards](#code-standards)
|
|
7. [AI Assistant Instructions](#ai-assistant-instructions)
|
|
8. [Release Process](#release-process)
|
|
|
|
---
|
|
|
|
## Project Overview
|
|
|
|
### Purpose
|
|
|
|
Renamer is a sophisticated Terminal User Interface (TUI) application for managing, viewing metadata, and renaming media files. Built with Python and the Textual framework.
|
|
|
|
**Dual-Mode Operation**:
|
|
- **Technical Mode**: Detailed technical metadata (video tracks, audio streams, codecs, bitrates)
|
|
- **Catalog Mode**: Media library catalog view with TMDB integration (posters, ratings, descriptions)
|
|
|
|
### Current Version
|
|
|
|
- **Version**: 0.7.0-dev (in development)
|
|
- **Python**: 3.11+
|
|
- **License**: Not specified
|
|
- **Repository**: `/home/sha/bin/renamer`
|
|
|
|
### Technology Stack
|
|
|
|
#### Core Dependencies
|
|
- **textual** (≥6.11.0): TUI framework
|
|
- **pymediainfo** (≥6.0.0): Media track analysis
|
|
- **mutagen** (≥1.47.0): Embedded metadata
|
|
- **python-magic** (≥0.4.27): MIME detection
|
|
- **langcodes** (≥3.5.1): Language code handling
|
|
- **requests** (≥2.31.0): HTTP for TMDB API
|
|
- **rich-pixels** (≥1.0.0): Terminal image display
|
|
- **pytest** (≥7.0.0): Testing framework
|
|
|
|
#### Dev Dependencies
|
|
- **mypy** (≥1.0.0): Type checking
|
|
|
|
#### System Requirements
|
|
- Python 3.11 or higher
|
|
- UV package manager (recommended)
|
|
- MediaInfo library (system dependency)
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
### Architectural Layers
|
|
|
|
```
|
|
┌─────────────────────────────────────────┐
|
|
│ TUI Layer (Textual) │
|
|
│ app.py, screens.py │
|
|
└─────────────────────────────────────────┘
|
|
↓
|
|
┌─────────────────────────────────────────┐
|
|
│ Service Layer │
|
|
│ FileTreeService, MetadataService, │
|
|
│ RenameService │
|
|
└─────────────────────────────────────────┘
|
|
↓
|
|
┌─────────────────────────────────────────┐
|
|
│ Extractor Layer │
|
|
│ MediaExtractor coordinates: │
|
|
│ - FilenameExtractor │
|
|
│ - MediaInfoExtractor │
|
|
│ - MetadataExtractor │
|
|
│ - FileInfoExtractor │
|
|
│ - TMDBExtractor │
|
|
│ - DefaultExtractor │
|
|
└─────────────────────────────────────────┘
|
|
↓
|
|
┌─────────────────────────────────────────┐
|
|
│ Formatter Layer │
|
|
│ FormatterApplier coordinates: │
|
|
│ - DataFormatters (size, duration) │
|
|
│ - TextFormatters (case, style) │
|
|
│ - MarkupFormatters (colors, bold) │
|
|
└─────────────────────────────────────────┘
|
|
↓
|
|
┌─────────────────────────────────────────┐
|
|
│ Utility & Cache Layer │
|
|
│ - PatternExtractor │
|
|
│ - LanguageCodeExtractor │
|
|
│ - FrameClassMatcher │
|
|
│ - Unified Cache Subsystem │
|
|
└─────────────────────────────────────────┘
|
|
```
|
|
|
|
### Design Patterns
|
|
|
|
1. **Protocol-Based Architecture**: `DataExtractor` Protocol defines extractor interface
|
|
2. **Coordinator Pattern**: `MediaExtractor` coordinates multiple extractors with priority system
|
|
3. **Strategy Pattern**: Cache key strategies for different data types
|
|
4. **Decorator Pattern**: `@cached_method()` for method-level caching
|
|
5. **Service Layer**: Business logic separated from UI
|
|
6. **Dependency Injection**: Services receive extractors/formatters as dependencies
|
|
|
|
---
|
|
|
|
## Core Components
|
|
|
|
### 1. Main Application (`renamer/app.py`)
|
|
|
|
**Class**: `RenamerApp(App)`
|
|
|
|
**Responsibilities**:
|
|
- TUI layout management (split view: file tree + details panel)
|
|
- Keyboard/mouse navigation
|
|
- Command palette integration (Ctrl+P)
|
|
- File operation coordination
|
|
- Efficient tree updates
|
|
|
|
**Key Features**:
|
|
- Two command providers: `AppCommandProvider`, `CacheCommandProvider`
|
|
- Dual-mode support (technical/catalog)
|
|
- Real-time metadata display
|
|
|
|
### 2. Service Layer (`renamer/services/`)
|
|
|
|
#### FileTreeService (`file_tree_service.py`)
|
|
- Directory scanning and validation
|
|
- Recursive tree building with filtering
|
|
- Media file detection (based on `MEDIA_TYPES`)
|
|
- Permission error handling
|
|
- Tree node searching by path
|
|
- Directory statistics
|
|
|
|
#### MetadataService (`metadata_service.py`)
|
|
- **Thread pool management** (ThreadPoolExecutor, configurable workers)
|
|
- **Thread-safe operations** with Lock
|
|
- Concurrent metadata extraction
|
|
- **Active extraction tracking** and cancellation
|
|
- Cache integration via decorators
|
|
- Synchronous and asynchronous modes
|
|
- Formatter coordination
|
|
- Error handling with callbacks
|
|
- Context manager support
|
|
|
|
#### RenameService (`rename_service.py`)
|
|
- Proposed name generation from metadata
|
|
- Filename validation and sanitization
|
|
- Invalid character removal (cross-platform)
|
|
- Reserved name checking (Windows compatibility)
|
|
- File conflict detection
|
|
- Atomic rename operations
|
|
- Dry-run mode
|
|
- Callback-based rename with success/error handlers
|
|
- Markup tag stripping
|
|
|
|
### 3. Extractor System (`renamer/extractors/`)
|
|
|
|
#### Base Protocol (`base.py`)
|
|
```python
|
|
class DataExtractor(Protocol):
|
|
"""Defines standard interface for all extractors"""
|
|
def extract_title(self) -> Optional[str]: ...
|
|
def extract_year(self) -> Optional[str]: ...
|
|
# ... 21 methods total
|
|
```
|
|
|
|
#### MediaExtractor (`extractor.py`)
|
|
**Coordinator class** managing priority-based extraction:
|
|
|
|
**Priority Order Examples**:
|
|
- Title: TMDB → Metadata → Filename → Default
|
|
- Year: Filename → Default
|
|
- Technical info: MediaInfo → Default
|
|
- File info: FileInfo → Default
|
|
|
|
**Usage**:
|
|
```python
|
|
extractor = MediaExtractor(Path("movie.mkv"))
|
|
title = extractor.get("title") # Tries sources in priority order
|
|
year = extractor.get("year", source="Filename") # Force specific source
|
|
```
|
|
|
|
#### Specialized Extractors
|
|
|
|
1. **FilenameExtractor** (`filename_extractor.py`)
|
|
- Parses metadata from filename patterns
|
|
- Detects year, resolution, source, codecs, edition
|
|
- Uses regex patterns and utility classes
|
|
- Handles Cyrillic normalization
|
|
- Extracts language codes with counts (e.g., "2xUKR_ENG")
|
|
|
|
2. **MediaInfoExtractor** (`mediainfo_extractor.py`)
|
|
- Uses PyMediaInfo library
|
|
- Extracts detailed track information
|
|
- Provides codec, bitrate, frame rate, resolution
|
|
- Frame class matching with tolerances
|
|
|
|
3. **MetadataExtractor** (`metadata_extractor.py`)
|
|
- Uses Mutagen library for embedded tags
|
|
- Extracts title, artist, duration
|
|
- Falls back to MIME type detection
|
|
- Handles multiple container formats
|
|
|
|
4. **FileInfoExtractor** (`fileinfo_extractor.py`)
|
|
- Basic file system information
|
|
- Size, modification time, paths
|
|
- Extension extraction
|
|
- Fast, no external dependencies
|
|
|
|
5. **TMDBExtractor** (`tmdb_extractor.py`)
|
|
- The Movie Database API integration
|
|
- Fetches title, year, ratings, overview, genres
|
|
- Downloads and caches posters
|
|
- Supports movies and TV shows
|
|
- Rate limiting and error handling
|
|
|
|
6. **DefaultExtractor** (`default_extractor.py`)
|
|
- Fallback extractor providing default values
|
|
- Returns None or empty collections
|
|
- Safe final fallback in extractor chain
|
|
|
|
### 4. Formatter System (`renamer/formatters/`)
|
|
|
|
#### Base Classes (`base.py`)
|
|
- `Formatter`: Base ABC with abstract `format()` method
|
|
- `DataFormatter`: For data transformations (sizes, durations, dates)
|
|
- `TextFormatter`: For text transformations (case changes)
|
|
- `MarkupFormatter`: For visual styling (colors, bold, links)
|
|
- `CompositeFormatter`: For chaining multiple formatters
|
|
|
|
#### FormatterApplier (`formatter.py`)
|
|
**Coordinator** ensuring correct formatter order:
|
|
|
|
**Order**: Data → Text → Markup
|
|
|
|
**Global Ordering**:
|
|
1. Data formatters (size, duration, date, track info)
|
|
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
|
|
|
|
### 5. Utility Modules (`renamer/utils/`)
|
|
|
|
#### PatternExtractor (`pattern_utils.py`)
|
|
**Centralized regex pattern matching**:
|
|
- Movie database ID extraction (TMDB, IMDB, Trakt, TVDB)
|
|
- Year extraction and validation
|
|
- Quality indicator detection
|
|
- Source indicator detection
|
|
- Bracketed content manipulation
|
|
- Position finding for year/quality/source
|
|
|
|
**Example**:
|
|
```python
|
|
extractor = PatternExtractor()
|
|
db_info = extractor.extract_movie_db_ids("[tmdbid-12345]")
|
|
# Returns: {'type': 'tmdb', 'id': '12345'}
|
|
```
|
|
|
|
#### LanguageCodeExtractor (`language_utils.py`)
|
|
**Language code processing**:
|
|
- Extract from brackets: `[UKR_ENG]` → `['ukr', 'eng']`
|
|
- Extract standalone codes from filename
|
|
- Handle count patterns: `[2xUKR_ENG]`
|
|
- Convert to ISO 639-3 codes
|
|
- Skip quality indicators and file extensions
|
|
- Format as language counts: `"2ukr,eng"`
|
|
|
|
**Example**:
|
|
```python
|
|
extractor = LanguageCodeExtractor()
|
|
langs = extractor.extract_from_brackets("[2xUKR_ENG]")
|
|
# Returns: ['ukr', 'ukr', 'eng']
|
|
```
|
|
|
|
#### FrameClassMatcher (`frame_utils.py`)
|
|
**Resolution/frame class matching**:
|
|
- Multi-step matching algorithm
|
|
- Height and width tolerance
|
|
- Aspect ratio calculation
|
|
- Scan type detection (progressive/interlaced)
|
|
- Standard resolution checking
|
|
- Nominal height/typical widths lookup
|
|
|
|
**Matching Strategy**:
|
|
1. Exact height + width match
|
|
2. Height match with aspect ratio validation
|
|
3. Closest height match
|
|
4. Non-standard quality indicator detection
|
|
|
|
### 6. Constants (`renamer/constants/`)
|
|
|
|
**Modular organization** (8 files):
|
|
|
|
1. **media_constants.py**: `MEDIA_TYPES` - Supported video formats
|
|
2. **source_constants.py**: `SOURCE_DICT` - Video source types
|
|
3. **frame_constants.py**: `FRAME_CLASSES`, `NON_STANDARD_QUALITY_INDICATORS`
|
|
4. **moviedb_constants.py**: `MOVIE_DB_DICT` - Database identifiers
|
|
5. **edition_constants.py**: `SPECIAL_EDITIONS` - Edition types
|
|
6. **lang_constants.py**: `SKIP_WORDS` - Words to skip in language detection
|
|
7. **year_constants.py**: `is_valid_year()`, dynamic year validation
|
|
8. **cyrillic_constants.py**: `CYRILLIC_TO_ENGLISH` - Character mappings
|
|
|
|
**Backward Compatibility**: All constants exported via `__init__.py`
|
|
|
|
### 7. Cache Subsystem (`renamer/cache/`)
|
|
|
|
**Unified, modular architecture**:
|
|
|
|
```
|
|
renamer/cache/
|
|
├── __init__.py # Exports and convenience functions
|
|
├── core.py # Core Cache class (thread-safe with RLock)
|
|
├── types.py # CacheEntry, CacheStats TypedDicts
|
|
├── strategies.py # Cache key generation strategies
|
|
├── managers.py # CacheManager for operations
|
|
└── decorators.py # Enhanced cache decorators
|
|
```
|
|
|
|
#### Cache Key Strategies
|
|
- `FilepathMethodStrategy`: For extractor methods
|
|
- `APIRequestStrategy`: For API responses
|
|
- `SimpleKeyStrategy`: For simple prefix+id patterns
|
|
- `CustomStrategy`: User-defined key generation
|
|
|
|
#### Cache Decorators
|
|
```python
|
|
@cached_method(ttl=3600) # Method caching
|
|
def extract_title(self):
|
|
...
|
|
|
|
@cached_api(service="tmdb", ttl=21600) # API caching
|
|
def fetch_movie_data(self, movie_id):
|
|
...
|
|
```
|
|
|
|
#### Cache Manager Operations
|
|
- `clear_all()`: Remove all cache entries
|
|
- `clear_by_prefix(prefix)`: Clear specific cache type
|
|
- `clear_expired()`: Remove expired entries
|
|
- `get_stats()`: Comprehensive statistics
|
|
- `clear_file_cache(file_path)`: Clear cache for specific file
|
|
- `compact_cache()`: Remove empty directories
|
|
|
|
#### Command Palette Integration
|
|
Access via Ctrl+P:
|
|
- Cache: View Statistics
|
|
- Cache: Clear All
|
|
- Cache: Clear Extractors / TMDB / Posters
|
|
- Cache: Clear Expired / Compact
|
|
|
|
#### Thread Safety
|
|
- All operations protected by `threading.RLock`
|
|
- Safe for concurrent extractor access
|
|
- Memory cache synchronized with file cache
|
|
|
|
### 8. UI Screens (`renamer/screens.py`)
|
|
|
|
1. **OpenScreen**: Directory selection dialog with validation
|
|
2. **HelpScreen**: Comprehensive help with key bindings
|
|
3. **RenameConfirmScreen**: File rename confirmation with error handling
|
|
4. **SettingsScreen**: Settings configuration interface
|
|
|
|
### 9. Settings System (`renamer/settings.py`)
|
|
|
|
**Configuration**: `~/.config/renamer/config.json`
|
|
|
|
**Options**:
|
|
```json
|
|
{
|
|
"mode": "technical", // or "catalog"
|
|
"cache_ttl_extractors": 21600, // 6 hours
|
|
"cache_ttl_tmdb": 21600, // 6 hours
|
|
"cache_ttl_posters": 2592000 // 30 days
|
|
}
|
|
```
|
|
|
|
Automatic save/load with defaults.
|
|
|
|
---
|
|
|
|
## Development Setup
|
|
|
|
### Installation
|
|
|
|
```bash
|
|
# Install UV
|
|
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
|
|
# Clone and sync
|
|
cd /home/sha/bin/renamer
|
|
uv sync
|
|
|
|
# Install dev dependencies
|
|
uv sync --extra dev
|
|
|
|
# Run from source
|
|
uv run python renamer/main.py [directory]
|
|
```
|
|
|
|
### Development Commands
|
|
|
|
```bash
|
|
# Run installed version
|
|
uv run renamer [directory]
|
|
|
|
# Run tests
|
|
uv run pytest
|
|
|
|
# Run tests with coverage
|
|
uv run pytest --cov=renamer
|
|
|
|
# Type checking
|
|
uv run mypy renamer/extractors/default_extractor.py
|
|
|
|
# Version management
|
|
uv run bump-version # Increment patch version
|
|
uv run release # Bump + sync + build
|
|
|
|
# Build distribution
|
|
uv build # Create wheel and tarball
|
|
|
|
# Install as global tool
|
|
uv tool install .
|
|
```
|
|
|
|
### Debugging
|
|
|
|
```bash
|
|
# Enable formatter logging
|
|
FORMATTER_LOG=1 uv run renamer /path/to/directory
|
|
# Creates formatter.log with detailed call traces
|
|
```
|
|
|
|
---
|
|
|
|
## Testing Strategy
|
|
|
|
### Test Organization
|
|
|
|
```
|
|
renamer/test/
|
|
├── datasets/ # Test data
|
|
│ ├── filenames/
|
|
│ │ ├── filename_patterns.json # 46 test cases
|
|
│ │ └── sample_files/ # Legacy reference
|
|
│ ├── mediainfo/
|
|
│ │ └── frame_class_tests.json # 25 test cases
|
|
│ └── sample_mediafiles/ # Generated (in .gitignore)
|
|
├── conftest.py # Fixtures and dataset loaders
|
|
├── test_cache_subsystem.py # 18 cache tests
|
|
├── test_services.py # 30+ service tests
|
|
├── test_utils.py # 70+ utility tests
|
|
├── test_formatters.py # 40+ formatter tests
|
|
├── test_filename_detection.py # Comprehensive filename parsing
|
|
├── test_filename_extractor.py # 368 extractor tests
|
|
├── test_mediainfo_*.py # MediaInfo tests
|
|
├── test_fileinfo_extractor.py # File info tests
|
|
└── test_metadata_extractor.py # Metadata tests
|
|
```
|
|
|
|
### Test Statistics
|
|
|
|
- **Total Tests**: 560 (1 skipped)
|
|
- **Service Layer**: 30+ tests
|
|
- **Utilities**: 70+ tests
|
|
- **Formatters**: 40+ tests
|
|
- **Extractors**: 400+ tests
|
|
- **Cache**: 18 tests
|
|
|
|
### Sample File Generation
|
|
|
|
```bash
|
|
# Generate 46 test files from filename_patterns.json
|
|
uv run python renamer/test/fill_sample_mediafiles.py
|
|
```
|
|
|
|
### Test Fixtures
|
|
|
|
```python
|
|
# Load test datasets
|
|
patterns = load_filename_patterns()
|
|
frame_tests = load_frame_class_tests()
|
|
dataset = load_dataset("custom_name")
|
|
file_path = get_test_file_path("movie.mkv")
|
|
```
|
|
|
|
### Running Tests
|
|
|
|
```bash
|
|
# All tests
|
|
uv run pytest
|
|
|
|
# Specific test file
|
|
uv run pytest renamer/test/test_services.py
|
|
|
|
# With verbose output
|
|
uv run pytest -xvs
|
|
|
|
# With coverage
|
|
uv run pytest --cov=renamer --cov-report=html
|
|
```
|
|
|
|
---
|
|
|
|
## Code Standards
|
|
|
|
### Python Standards
|
|
|
|
- **Version**: Python 3.11+
|
|
- **Style**: PEP 8 guidelines
|
|
- **Type Hints**: Encouraged for all public APIs
|
|
- **Docstrings**: Google-style format
|
|
- **Pathlib**: For all file operations
|
|
- **Exception Handling**: Specific exceptions (no bare `except:`)
|
|
|
|
### Docstring Format
|
|
|
|
```python
|
|
def example_function(param1: int, param2: str) -> bool:
|
|
"""Brief description of function.
|
|
|
|
Longer description if needed, explaining behavior,
|
|
edge cases, or important details.
|
|
|
|
Args:
|
|
param1: Description of param1
|
|
param2: Description of param2
|
|
|
|
Returns:
|
|
Description of return value
|
|
|
|
Raises:
|
|
ValueError: When param1 is negative
|
|
|
|
Example:
|
|
>>> example_function(5, "test")
|
|
True
|
|
"""
|
|
pass
|
|
```
|
|
|
|
### Type Hints
|
|
|
|
```python
|
|
from typing import Optional
|
|
|
|
# Function type hints
|
|
def extract_title(self) -> Optional[str]:
|
|
...
|
|
|
|
# Union types (Python 3.10+)
|
|
def extract_movie_db(self) -> list[str] | None:
|
|
...
|
|
|
|
# Generic types
|
|
def extract_tracks(self) -> list[dict]:
|
|
...
|
|
```
|
|
|
|
### Logging Strategy
|
|
|
|
**Levels**:
|
|
- **Debug**: Language code conversions, metadata reads, MIME detection
|
|
- **Warning**: Network failures, API errors, MediaInfo parse failures
|
|
- **Error**: Formatter application failures
|
|
|
|
**Usage**:
|
|
```python
|
|
import logging
|
|
logger = logging.getLogger(__name__)
|
|
|
|
logger.debug(f"Converted {lang_code} to {iso3_code}")
|
|
logger.warning(f"TMDB API request failed: {e}")
|
|
logger.error(f"Error applying {formatter.__name__}: {e}")
|
|
```
|
|
|
|
### Error Handling
|
|
|
|
**Guidelines**:
|
|
- Catch specific exceptions: `(LookupError, ValueError, AttributeError)`
|
|
- Log all caught exceptions with context
|
|
- Network errors: `(requests.RequestException, ValueError)`
|
|
- Always close file handles (use context managers)
|
|
|
|
**Example**:
|
|
```python
|
|
try:
|
|
lang_obj = langcodes.Language.get(lang_code.lower())
|
|
return lang_obj.to_alpha3()
|
|
except (LookupError, ValueError, AttributeError) as e:
|
|
logger.debug(f"Invalid language code '{lang_code}': {e}")
|
|
return None
|
|
```
|
|
|
|
### Architecture Patterns
|
|
|
|
1. **Extractor Pattern**: Each extractor focuses on one data source
|
|
2. **Formatter Pattern**: Formatters handle display logic, extractors handle data
|
|
3. **Separation of Concerns**: Data extraction → formatting → display
|
|
4. **Dependency Injection**: Extractors and formatters are modular
|
|
5. **Configuration Management**: Settings class for all config
|
|
|
|
### Best Practices
|
|
|
|
- **Simplicity**: Avoid over-engineering, keep solutions simple
|
|
- **Minimal Changes**: Only modify what's explicitly requested
|
|
- **Validation**: Only at system boundaries (user input, external APIs)
|
|
- **Trust Internal Code**: Don't add unnecessary error handling
|
|
- **Delete Unused Code**: No backwards-compatibility hacks
|
|
- **No Premature Abstraction**: Three similar lines > premature abstraction
|
|
|
|
---
|
|
|
|
## AI Assistant Instructions
|
|
|
|
### Core Principles
|
|
|
|
1. **Read Before Modify**: Always read files before suggesting modifications
|
|
2. **Follow Existing Patterns**: Understand established architecture before changes
|
|
3. **Test Everything**: Run `uv run pytest` after all changes
|
|
4. **Simplicity First**: Avoid over-engineering solutions
|
|
5. **Document Changes**: Update relevant documentation
|
|
|
|
### When Adding Features
|
|
|
|
1. Read existing code and understand architecture
|
|
2. Check `REFACTORING_PROGRESS.md` for pending tasks
|
|
3. Implement features incrementally
|
|
4. Test with real media files
|
|
5. Ensure backward compatibility
|
|
6. Update documentation
|
|
7. Update tests as needed
|
|
8. Run `uv run release` before committing
|
|
|
|
### When Debugging
|
|
|
|
1. Enable formatter logging: `FORMATTER_LOG=1`
|
|
2. Check cache state (clear if stale data suspected)
|
|
3. Verify file permissions
|
|
4. Test with sample filenames first
|
|
5. Check logs in `formatter.log`
|
|
|
|
### When Refactoring
|
|
|
|
1. Maintain backward compatibility unless explicitly breaking
|
|
2. Update tests to reflect refactored code
|
|
3. Check all formatters (formatting is centralized)
|
|
4. Verify extractor chain (ensure data flow intact)
|
|
5. Run full test suite
|
|
|
|
### Common Pitfalls to Avoid
|
|
|
|
- ❌ Don't create new files unless absolutely necessary
|
|
- ❌ Don't add features beyond what's requested
|
|
- ❌ Don't skip testing with real files
|
|
- ❌ Don't forget to update version number for releases
|
|
- ❌ Don't commit secrets or API keys
|
|
- ❌ Don't use deprecated Textual APIs
|
|
- ❌ Don't use bare `except:` clauses
|
|
- ❌ Don't use command-line tools when specialized tools exist
|
|
|
|
### Tool Usage
|
|
|
|
- **Read files**: Use `Read` tool, not `cat`
|
|
- **Edit files**: Use `Edit` tool, not `sed`
|
|
- **Write files**: Use `Write` tool, not `echo >>`
|
|
- **Search files**: Use `Glob` tool, not `find`
|
|
- **Search content**: Use `Grep` tool, not `grep`
|
|
- **Run commands**: Use `Bash` tool for terminal operations only
|
|
|
|
### Git Workflow
|
|
|
|
**Commit Standards**:
|
|
- Clear, descriptive messages
|
|
- Focus on "why" not "what"
|
|
- One logical change per commit
|
|
|
|
**Commit Message Format**:
|
|
```
|
|
type: Brief description (imperative mood)
|
|
|
|
Longer explanation if needed.
|
|
|
|
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
|
|
|
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
```
|
|
|
|
**Safety Protocol**:
|
|
- ❌ NEVER update git config
|
|
- ❌ NEVER run destructive commands without explicit request
|
|
- ❌ NEVER skip hooks (--no-verify, --no-gpg-sign)
|
|
- ❌ NEVER force push to main/master
|
|
- ❌ Avoid `git commit --amend` unless conditions met
|
|
|
|
### Creating Pull Requests
|
|
|
|
1. Run `git status`, `git diff`, `git log` to understand changes
|
|
2. Analyze ALL commits that will be included
|
|
3. Draft comprehensive PR summary
|
|
4. Create PR using:
|
|
```bash
|
|
gh pr create --title "Title" --body "$(cat <<'EOF'
|
|
## Summary
|
|
- Bullet points of changes
|
|
|
|
## Test plan
|
|
- Testing checklist
|
|
|
|
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
|
EOF
|
|
)"
|
|
```
|
|
|
|
---
|
|
|
|
## Release Process
|
|
|
|
### Version Management
|
|
|
|
**Version Scheme**: SemVer (MAJOR.MINOR.PATCH)
|
|
|
|
**Commands**:
|
|
```bash
|
|
# Bump patch version (0.6.0 → 0.6.1)
|
|
uv run bump-version
|
|
|
|
# Full release process
|
|
uv run release # Bump + sync + build
|
|
```
|
|
|
|
### Release Checklist
|
|
|
|
- [ ] All tests passing: `uv run pytest`
|
|
- [ ] Type checking passes: `uv run mypy renamer/`
|
|
- [ ] Documentation updated (CHANGELOG.md, README.md)
|
|
- [ ] Version bumped in `pyproject.toml`
|
|
- [ ] Dependencies synced: `uv sync`
|
|
- [ ] Build successful: `uv build`
|
|
- [ ] Install test: `uv tool install .`
|
|
- [ ] Manual testing with real media files
|
|
|
|
### Build Artifacts
|
|
|
|
```
|
|
dist/
|
|
├── renamer-0.7.0-py3-none-any.whl # Wheel distribution
|
|
└── renamer-0.7.0.tar.gz # Source distribution
|
|
```
|
|
|
|
---
|
|
|
|
## API Integration
|
|
|
|
### TMDB API
|
|
|
|
**Configuration**:
|
|
- API key stored in `renamer/secrets.py`
|
|
- Base URL: `https://api.themoviedb.org/3/`
|
|
- Image base URL for poster downloads
|
|
|
|
**Endpoints Used**:
|
|
- Search: `/search/movie`
|
|
- Movie details: `/movie/{id}`
|
|
|
|
**Rate Limiting**: Handled gracefully with error fallback
|
|
|
|
**Caching**:
|
|
- API responses cached for 6 hours
|
|
- Posters cached for 30 days
|
|
- Cache location: `~/.cache/renamer/tmdb/`, `~/.cache/renamer/posters/`
|
|
|
|
---
|
|
|
|
## File Operations
|
|
|
|
### Directory Scanning
|
|
|
|
- Recursive search for supported video formats
|
|
- File tree representation with hierarchical structure
|
|
- Efficient tree updates on file operations
|
|
- Permission error handling
|
|
|
|
### File Renaming
|
|
|
|
**Process**:
|
|
1. Select file in tree
|
|
2. Press `r` to initiate rename
|
|
3. Review proposed name (current vs proposed)
|
|
4. Confirm with `y` or cancel with `n`
|
|
5. Tree updates in-place without full reload
|
|
|
|
**Proposed Name Format**:
|
|
```
|
|
Title (Year) [Resolution Source Edition].ext
|
|
```
|
|
|
|
**Sanitization**:
|
|
- Invalid characters removed (cross-platform)
|
|
- Reserved names checked (Windows compatibility)
|
|
- Markup tags stripped
|
|
- Length validation
|
|
|
|
### Metadata Caching
|
|
|
|
- First extraction cached for 6 hours
|
|
- TMDB data cached for 6 hours
|
|
- Posters cached for 30 days
|
|
- Force refresh with `f` command
|
|
- Cache invalidated on file rename
|
|
|
|
---
|
|
|
|
## Keyboard Commands
|
|
|
|
| Key | Action |
|
|
|-----|--------|
|
|
| `q` | Quit application |
|
|
| `o` | Open directory |
|
|
| `s` | Scan/rescan directory |
|
|
| `f` | Refresh metadata for selected file |
|
|
| `r` | Rename file with proposed name |
|
|
| `p` | Toggle tree expansion |
|
|
| `m` | Toggle mode (technical/catalog) |
|
|
| `h` | Show help screen |
|
|
| `Ctrl+S` | Open settings |
|
|
| `Ctrl+P` | Open command palette |
|
|
|
|
---
|
|
|
|
## Known Issues & Limitations
|
|
|
|
### Current Limitations
|
|
|
|
- TMDB API requires internet connection
|
|
- Poster display requires terminal with image support
|
|
- Some special characters in filenames need sanitization
|
|
- Large directories may have initial scan delay
|
|
|
|
### Performance Notes
|
|
|
|
- In-memory cache reduces repeated extraction overhead
|
|
- File cache persists across sessions
|
|
- Tree updates optimized for rename operations
|
|
- TMDB requests throttled to respect API limits
|
|
- Large directory scans use async/await patterns
|
|
|
|
---
|
|
|
|
## Security Considerations
|
|
|
|
- Input sanitization for filenames (see `ProposedNameFormatter`)
|
|
- No shell command injection risks
|
|
- Safe file operations (pathlib, proper error handling)
|
|
- TMDB API key should not be committed (stored in `secrets.py`)
|
|
- Cache directory permissions should be user-only
|
|
|
|
---
|
|
|
|
## Project History
|
|
|
|
### Evolution
|
|
|
|
- Started as simple file renamer
|
|
- Added metadata extraction (MediaInfo, Mutagen)
|
|
- Expanded to TUI with Textual framework
|
|
- Added filename parsing intelligence
|
|
- Integrated TMDB for catalog mode
|
|
- Added settings and caching system
|
|
- Implemented poster display with rich-pixels
|
|
- Added dual-mode interface (technical/catalog)
|
|
- Phase 1-3 refactoring (2025-12-31 to 2026-01-01)
|
|
|
|
### Version Milestones
|
|
|
|
- **0.2.x**: Initial TUI with basic metadata
|
|
- **0.3.x**: Enhanced extractors and formatters
|
|
- **0.4.x**: Added TMDB integration
|
|
- **0.5.x**: Settings, caching, catalog mode, poster display
|
|
- **0.6.0**: Cache subsystem, service layer, protocols
|
|
- **0.7.0-dev**: Complete refactoring (in progress)
|
|
|
|
---
|
|
|
|
## Resources
|
|
|
|
### External Documentation
|
|
|
|
- [Textual Documentation](https://textual.textualize.io/)
|
|
- [PyMediaInfo Documentation](https://pymediainfo.readthedocs.io/)
|
|
- [Mutagen Documentation](https://mutagen.readthedocs.io/)
|
|
- [TMDB API Documentation](https://developers.themoviedb.org/3)
|
|
- [UV Documentation](https://docs.astral.sh/uv/)
|
|
- [Python Type Hints](https://docs.python.org/3/library/typing.html)
|
|
- [Mypy Documentation](https://mypy.readthedocs.io/)
|
|
|
|
### Internal Documentation
|
|
|
|
- **README.md**: User guide and quick start
|
|
- **INSTALL.md**: Installation methods
|
|
- **DEVELOP.md**: Developer setup and debugging
|
|
- **CHANGELOG.md**: Version history and changes
|
|
- **REFACTORING_PROGRESS.md**: Future refactoring plans
|
|
- **ToDo.md**: Current task list
|
|
|
|
---
|
|
|
|
**Last Updated**: 2026-01-01
|
|
**Maintainer**: sha
|
|
**For**: AI Assistants and Developers
|
|
**Repository**: `/home/sha/bin/renamer`
|