Files
dl-organizer/CLAUDE.md
T

272 lines
12 KiB
Markdown
Raw Normal View History

# CLAUDE.md
## Documentation Status
2026-04-07 11:07:01 +08:00
- Synchronized with post-refactor baseline on 2026-04-07 (see `CHANGELOG.md`).
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
Video Library Manager (VLM) is a Python CLI tool for managing personal video collections with a safety-first, human-in-the-loop approach. All file operations are reversible, require explicit confirmation, and generate reviewable execution plans before making changes.
## Development Commands
2026-02-13 13:36:39 +08:00
**Important:** This project uses `uv` for Python package management. All commands should be run with `uv run` to ensure they execute in the correct project environment with proper dependencies.
### Installation
```bash
# Install package in editable mode
uv pip install -e .
# Install with dev dependencies (pytest, hypothesis)
uv pip install -e ".[dev]"
2026-04-07 08:07:18 +08:00
# Optional Textual UI for `vlm review-plan --tui`
uv pip install -e ".[tui]"
```
2026-04-07 11:07:01 +08:00
The default CLI path does not require Textual; it is imported only when `uv run vlm review-plan --tui` is used.
### Testing
```bash
2026-02-13 13:36:39 +08:00
# Run all tests (use uv run to execute in the project environment)
uv run pytest
# Run specific test file
2026-02-13 13:36:39 +08:00
uv run pytest tests/test_scanner.py
# Run with verbose output
2026-02-13 13:36:39 +08:00
uv run pytest -v
# Run with quiet output (summary only)
uv run pytest -q
# Count total test cases
uv run pytest --collect-only -q
```
### Running the CLI
```bash
2026-02-13 13:36:39 +08:00
# Verify CLI works (use uv run for proper environment)
uv run vlm --help
# Initialize config
2026-02-13 13:36:39 +08:00
uv run vlm config init
# Common workflow
2026-04-07 11:07:01 +08:00
uv run vlm scan # Discover files
uv run vlm parse # Extract identities (v1 schema)
uv run vlm parse --inventory artifacts/inventory.csv # Embed metadata for quality-aware duplicate handling
uv run vlm enrich # (Optional) Enrich titles/reputation via TMDB
uv run vlm enrich --refresh-all # Force full refresh (ignore cache)
uv run vlm analyze # Detect gaps/duplicates
uv run vlm plan # Generate execution plan
uv run vlm plan --analysis artifacts/analysis.json # Generate plan with duplicate resolution
uv run vlm review-plan # Export CSV + terminal review preview
uv run vlm review-plan --tui # Optional full-screen review UI
uv run vlm apply-review # Sync edited CSV decisions back into the plan
uv run vlm execute # Dry-run (default)
uv run vlm execute --confirm # Actually execute
uv run vlm rollback # Undo executed operations
# Reporting
2026-02-13 13:36:39 +08:00
uv run vlm report summary # Overview statistics
uv run vlm report inventory # File inventory
uv run vlm report completeness # Series with missing episodes
uv run vlm report duplicates # Duplicate files with quality comparison
# Quarantine management
2026-02-13 13:36:39 +08:00
uv run vlm quarantine list # List quarantined files
uv run vlm quarantine add <file> --reason "duplicate"
uv run vlm quarantine restore <file>
# State management
2026-02-13 13:36:39 +08:00
uv run vlm state show <file>
uv run vlm state set <file> --status reviewed
```
## Architecture
### Core Workflow
VLM follows a read-first, multi-stage pipeline:
2026-04-07 11:07:01 +08:00
1. **Scan** → discovers video files, extracts metadata via ffprobe (optional), saves to `artifacts/inventory.csv`
2. **Parse** → extracts titles/years/seasons/episodes from filenames, saves to `artifacts/identities.json`
- Use `--inventory artifacts/inventory.csv` to embed video metadata (v2 schema) for accurate duplicate resolution by quality
- Without `--inventory`, produces v1 schema (lightweight, no embedded metadata)
2026-04-07 11:07:01 +08:00
3. **Enrich** (optional) → adds bilingual titles and reputation (TMDB); updates `artifacts/identities.json` in place; uses SQLite cache for incremental runs
4. **Analyze** → detects episode gaps and duplicates, saves to `artifacts/analysis.json`
5. **Plan** → generates reviewable execution plan (`artifacts/plan.json`) with file operations
6. **Review / Apply Review** → previews high-risk operations in terminal or optional TUI, then syncs edited CSV decisions back into the plan when needed
7. **Execute** → performs file operations (dry-run by default, `--confirm` to execute)
8. **Rollback** → reverses executed operations (best-effort)
### Module Organization
- `cli.py` - Click-based CLI interface, global options, command registration
- `context.py` - CLIContext (config, paths) and pass_context for commands
- `commands/` - Command implementations (scan, parse, enrich, analyze, plan, execute/rollback)
- `scanner.py` - File discovery using system `find` command, metadata extraction via ffprobe
- `parser.py` - Filename parsing using regex patterns (movies: title + year, series: SxxExx)
- `enrichment.py` - Enrichment pipeline; `cache.py` - SQLite cache; `providers/` - TMDB etc.
2026-04-07 11:07:01 +08:00
- `io.py` - Unified JSON/CSV I/O helpers, including validated typed plan loading/saving
- `utils.py` - UTC time, format_size, shared helpers
- `analysis.py` - Completeness checking (episode gaps) and duplicate detection
2026-04-07 11:07:01 +08:00
- `duplicate_resolve.py` - Duplicate group resolution with explicit failures for unresolved strategies/data
- `planner.py` - Execution plan generation with conflict detection and manual-review duplicate fallback
- `executor.py` - File operations (move/rename/quarantine) with rollback logging and library-root safety checks
- `quarantine.py` - Quarantine management with manifest tracking
- `state.py` - File state tracking across workflow stages
- `reports.py` - Report generation (inventory, completeness, duplicates, summary)
- `config.py` - YAML configuration loading and validation
- `models.py` - Dataclass definitions for all data structures
- `logging_config.py` - Logging setup with fallback to console if file logging fails
### Key Data Structures
All defined in `models.py`:
- `VideoFile` - represents discovered video file with metadata
- `MovieIdentity` / `SeriesIdentity` - parsed identity with confidence score
- `ExecutionPlan` - collection of file operations with summary
- `FileOperation` - single operation (move/rename/quarantine/no-op) with conflict detection
- `RollbackLog` - log of executed operations for reversal
- `QuarantineEntry` - quarantined file with original location
- `FileState` - workflow state (reviewed/ignored/planned/executed/quarantined)
### File Categorization
Based on top-level directory within library root, matched against configured category mappings (case-insensitive).
Files in unmapped directories are categorized as "other" and skipped by planner/quarantine operations.
### Parsing Patterns (Hardcoded)
**Movies** (high confidence):
- `{title} ({year})`
- `{title}.{year}`
**Series** (high confidence):
- `S{season:02d}E{episode:02d}`
- `{season}x{episode}`
Patterns are hardcoded in parser.py, not user-configurable.
### Configuration
Default location: `~/.vlm/config.yaml`
Key settings:
- `library_root` - root directory to scan (required)
- `video_extensions` - list of extensions to recognize
- `templates.movie_dir` / `templates.series_dir` - directory structure templates
- `templates.movie_filename` / `templates.series_filename` - filename templates
- `quarantine_dir` - name of quarantine directory (default: `.quarantine`)
- `log_level` - logging verbosity
- `categories` - mapping of category names to directory name lists
- `enrichment` (or `enrich`) - TMDB/api_keys, cache_db, translation, reputation; see README for full schema
- `plan.duplicate_keep` - when using `vlm plan --analysis`: `by_quality` (resolution > source > codec > size), `by_reputation` (default), `first_seen`, or `manual`
### Category Mappings
Categories are determined by matching the top-level directory name against configured mappings:
**Default mappings:**
```yaml
categories:
movie: [movie]
series: [series]
anime: [anime]
```
**Custom mappings** support multiple directory names per category:
```yaml
categories:
movie: [movie, movies, films]
series: [series, tv, shows, television]
anime: [anime]
```
This allows files in `/library/movies/` or `/library/films/` to be recognized as the "movie" category. Directory matching is case-insensitive.
**Migration Note:** If you have existing directories with non-standard names (like "movies" or "tv"), update your config.yaml and re-run `vlm scan` to fix categorization. No files will be moved.
### Schema Versioning
**identities.json Schema Versions:**
- **v1** (default without `--inventory`): Lightweight schema without embedded metadata
- Records contain: path, filename, category, title, year/season/episodes, confidence, needs_review
- VideoFile objects reconstructed with defaults: size_bytes=0, resolution=None, codec=None
- Suitable for basic organization workflows
- **v2** (with `--inventory`): Enhanced schema with embedded video metadata
- All v1 fields plus `video_metadata` object containing:
- size_bytes, modified_timestamp, resolution, codec, duration_seconds, bitrate_kbps
- Enables accurate duplicate resolution by quality (compare resolution, codec, file size)
- Required for `by_quality` duplicate resolution strategy
- Backward compatible: v1 files load without errors
**Implementation Details:**
- `_video_file_from_record()` in io.py extracts embedded metadata if present
- Parse command with `--inventory` flag loads inventory.csv and embeds metadata in output
- Schema version stored in `vlm_schema_version` field at root level of identities.json
### File Discovery
Uses system `find` command for speed, falls back to Python recursion if unavailable. Hidden paths (starting with `.`) are skipped automatically.
### Timestamp Handling
All timestamps stored in UTC using ISO 8601 format (`YYYY-MM-DDTHH:MM:SS`). The scanner normalizes naive timestamps to UTC using system timezone.
### Logging
Logs to `~/.vlm/vlm.log` by default. If log directory is unwritable, falls back to console-only logging and continues execution (does not fail).
### Error Handling
- Configuration errors: display helpful message, create default config, continue
- Missing ffprobe: skip metadata extraction, log debug message, continue
- File access errors: log error, skip file, continue scanning
- Invalid YAML: display error, use default config
## Testing Guidelines
- Framework: pytest with hypothesis for property-based tests
- Test file naming: `test_*.py`
- Test function naming: `test_*`
- Test class naming: `Test*`
- Use `CliRunner` for CLI integration tests
- Prefer narrow unit tests for module logic plus targeted CLI integration tests
- Add tests with each behavior change, including error paths and edge cases
## Important Implementation Notes
### Safety Protocol
- NEVER permanently delete files - use quarantine instead
- All file operations create rollback logs when executed with --confirm
- Execution plans detect destination conflicts and mark operations
- Default mode is dry-run; --confirm required for actual execution
- Quarantine is reversible via restore command
### Anime Handling
Anime files are discovered and categorized but NOT parsed in v1 (deferred for future implementation).
### State Management
State tracking is optional and allows marking files as reviewed/ignored/planned/executed/quarantined. State is persisted to `~/.vlm/state.json`.
### Quarantine Constraints
Only movie and series files can be quarantined in v1 (anime and other categories rejected).
### Template Variables
Available for path/filename templates:
- Movies: `{title}`, `{year}`, `{ext}`
- Series: `{title}`, `{season}`, `{episode}`, `{ext}`
Format specifiers like `{season:02d}` are supported.
## Code Style
- Python 3.10+ idioms
- 4-space indentation
- PEP 8 naming: `snake_case` for functions/variables, `PascalCase` for classes, `UPPER_SNAKE_CASE` for constants
- Type hints for public functions and non-trivial internal APIs
- Modules focused on single responsibility
- No formatter/linter enforced - maintain consistency with existing files
## Commit Guidelines
- Clear, imperative commit subjects (e.g., "fix logging fallback for unwritable log dir")
- Keep commits focused - avoid mixing refactors and behavior changes
- Include co-author tag: `Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>`