# CLAUDE.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 ### Installation ```bash # Install package in editable mode uv pip install -e . # Install with dev dependencies (pytest, hypothesis) uv pip install -e ".[dev]" ``` ### Testing ```bash # Run all tests pytest # Run specific test file pytest tests/test_scanner.py # Run with verbose output pytest -v ``` ### Running the CLI ```bash # Verify CLI works vlm --help # Initialize config vlm config init # Common workflow vlm scan # Discover files vlm parse # Extract identities vlm enrich # (Optional) Enrich titles/reputation via TMDB vlm analyze # Detect gaps/duplicates vlm plan # Generate execution plan vlm execute # Dry-run (default) vlm execute --confirm # Actually execute ``` ## Architecture ### Core Workflow VLM follows a read-first, multi-stage pipeline: 1. **Scan** → discovers video files, extracts metadata via ffprobe (optional), saves to inventory.csv 2. **Parse** → extracts titles/years/seasons/episodes from filenames, saves to identities.json 3. **Enrich** (optional) → adds bilingual titles and reputation (TMDB); updates identities.json in place; uses SQLite cache for incremental runs 4. **Analyze** → detects episode gaps and duplicates, saves to analysis.json 5. **Plan** → generates reviewable execution plan (plan.json) with file operations 6. **Execute** → performs file operations (dry-run by default, --confirm to execute) 7. **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, analyze, plan) - `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. - `io.py` - Load/save identities JSON/CSV, plan/analysis input helpers - `utils.py` - UTC time, format_size, shared helpers - `analysis.py` - Completeness checking (episode gaps) and duplicate detection - `duplicate_resolve.py` - Duplicate group resolution (by_quality, by_reputation, first_seen, manual) - `planner.py` - Execution plan generation with conflict detection - `executor.py` - File operations (move/rename/quarantine) with rollback logging - `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. ### 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 `