Files
dl-organizer/CLAUDE.md
T
windyboyandClaude Sonnet 4.5 065195b83b Fix identity reconstruction metadata loss with v2 schema
Implements identities.json v2 schema with embedded video metadata to fix
duplicate resolution by quality, which previously failed due to VideoFile
objects being reconstructed with hardcoded defaults (size_bytes=0,
resolution=None, codec=None).

Changes:
- Add --inventory flag to vlm parse command to embed video metadata
- Update _video_file_from_record() to extract embedded metadata if present
- Add vlm_schema_version field to identities.json (v1.0 or v2.0)
- Maintain backward compatibility with v1 files (no metadata)

Schema v2 format:
- Embeds video_metadata object in each record (movies/series)
- Contains: size_bytes, modified_timestamp, resolution, codec,
  duration_seconds, bitrate_kbps
- Enables accurate quality comparison during duplicate analysis

Testing:
- Added comprehensive unit tests for io.py functions
- Added CLI integration tests for parse command
- Added end-to-end tests for duplicate quality comparison
- All 437 existing tests still pass (1 pre-existing failure in executor)

Documentation:
- Updated README.md with --inventory usage examples
- Updated CLAUDE.md with schema versioning details
- Added workflow examples showing metadata embedding

This fix resolves the critical P0 issue where duplicate resolution by_quality
strategy failed completely due to missing video metadata in reconstructed
VideoFile objects.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-13 09:44:51 +08:00

252 lines
10 KiB
Markdown

# 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 (v1 schema)
vlm parse --inventory inventory.csv # Extract identities with embedded metadata (v2 schema, recommended)
vlm enrich # (Optional) Enrich titles/reputation via TMDB
vlm enrich --refresh-all # Force full refresh (ignore cache)
vlm analyze # Detect gaps/duplicates
vlm plan # Generate execution plan
vlm plan --analysis analysis.json # Generate plan with duplicate resolution
vlm execute # Dry-run (default)
vlm execute --confirm # Actually execute
vlm rollback # Undo executed operations
# Reporting
vlm report summary # Overview statistics
vlm report inventory # File inventory
vlm report completeness # Series with missing episodes
vlm report duplicates # Duplicate files with quality comparison
# Quarantine management
vlm quarantine list # List quarantined files
vlm quarantine add <file> --reason "duplicate"
vlm quarantine restore <file>
# State management
vlm state show <file>
vlm state set <file> --status reviewed
```
## 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
- Use `--inventory inventory.csv` to embed video metadata (v2 schema) for accurate duplicate resolution by quality
- Without `--inventory`, produces v1 schema (lightweight, no embedded metadata)
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.
### 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>`