This file provides guidance to LLMs when working with code in this repository.
mnamer (media renamer) is a command-line utility for organizing media files. It parses filenames for metadata using guessit, queries metadata providers (TMDb, OMDb, TVDb, TvMaze), and intelligently renames/moves files based on configurable templates.
This project requires Python 3.12+ and uses uv as the package manager:
# Install dependencies
uv sync --dev
# Run mnamer locally
uv run mnamer [args]The test suite is organized by pytest markers. Keep local tests free of network access; network and e2e tests can be flaky because they exercise providers and the CLI workflow.
# Run local unit tests (no network)
uv run pytest -m local
# Run network tests (requires internet, may be flaky)
uv run pytest -m network --reruns 3
# Run end-to-end tests
uv run pytest -m e2e --reruns 3
# Run all tests with coverage
uv run pytest --cov=./ --cov-report=term-missing
# Run a specific test file
uv run pytest tests/local/test_metadata.py
# Run a specific test function
uv run pytest tests/local/test_metadata.py::test_function_nameRegistered markers are declared in pytest.ini: local, network, e2e,
plus provider markers omdb, tmdb, tvdb, and tvmaze.
# Check code with ruff
uv run ruff check mnamer tests
# Format code with ruff
uv run ruff format mnamer tests
# Type check with mypy
uv run mypy mnamer tests- Entry Point (
__main__.py:main): Loads settings and initializes the CLI frontend - Frontend (
frontends.py:Cli): Orchestrates the file processing workflow - Target (
target.py:Target): Represents a media file, manages its metadata and relocation - Metadata (
metadata.py): Dataclasses for storing parsed and enriched metadataMetadataMovie: Movie-specific fields (name, year, id_imdb, id_tmdb)MetadataEpisode: TV episode fields (series, season, episode, id_tvdb, id_tvmaze)
- Providers (
providers.py): High-level interface for querying metadata APIsTmdb,Omdb: Movie providersTvdb,Tvmaze: TV episode providers
- Endpoints (
endpoints.py): Low-level API request functions and response TypedDicts
-
Metadata Parsing: Uses
guessitlibrary to extract metadata from filenames. TheTarget._parse()method converts guessit output intoMetadataobjects. -
Target Discovery:
Target.populate_paths()crawls positional targets, applies--recurse,--ignore,--mask, de-duplicates paths, and filters by--mediawhen supplied. -
Provider System: Providers are registered per-provider in a class variable cache (
Target._providers).Provider.provider_factory()instantiates providers based on theProviderTypeenum and settings. -
Settings Management:
SettingStoreloads configuration from both CLI arguments (argument.py:ArgLoader) and JSON config files (.mnamer-v2.json). CLI args take precedence over config files. Config-only fields include API keys and replacement maps. -
Template Formatting:
MetadataMovie.__format__()andMetadataEpisode.__format__()use_MetaFormatterto substitute template variables like{name},{season:02}, and{extension}. Templates are configured per media type via settings. -
File Relocation:
Target.destinationcombines the optionalmovie_directoryorepisode_directorysetting with the matching format template, then applies replacement, scene/lowercase settings, and filename sanitization.Target.relocate()creates destination directories and moves the file.
-
Subtitle Handling: Subtitle files (
.srt,.idx,.sub) are detected viais_subtitle(). They use the same format pattern as their media type and, when subtitle language is known, prefix the extension with the 2-letter language code (e.g.,.en.srt). -
Language Support: The
Languageclass (inlanguage.py) wraps babelfish for language code conversion. Providers return metadata in the language specified by--languagesetting. -
Request Caching: API requests go through
utils.get_session(), arequests-cacheCachedSessionstored under the user cache directory for six days. Cache can be cleared with the--clear-cachedirective or bypassed per run with--no-cache. -
Error Handling: Custom exceptions in
exceptions.pydistinguish between network errors (MnamerNetworkException), missing results (MnamerNotFoundException), and user actions (MnamerSkipException,MnamerAbortException). -
Test Organization:
tests/local/: Pure unit tests, no network or filesystem side effectstests/network/: Integration tests hitting real APIs (may be flaky)tests/e2e/: End-to-end tests with actual file operations
- Config File:
.mnamer-v2.jsonin the current or parent directories, or the explicit path from--config-path - Settings Precedence: CLI arguments > config file > defaults
- Directives vs Parameters: Directives (like
--test,--id-tmdb) are one-time overrides not stored in config files - Config-only Fields:
api_key_omdb,api_key_tmdb,api_key_tvdb,api_key_tvmaze,replace_before, andreplace_afterare only loaded from config/defaults, not CLI flags
Providers load API keys through Provider.from_settings() using config-only
fields named api_key_<provider>. If unset, provider classes fall back to
environment variables (API_KEY_OMDB, API_KEY_TMDB, API_KEY_TVDB,
API_KEY_TVMAZE) and then bundled defaults where present.
Check provider classes in providers.py for API key handling via from_settings() class method.