mnamer 2.7.1.dev19__tar.gz → 2.7.1.dev21__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. mnamer-2.7.1.dev21/AGENTS.md +123 -0
  2. mnamer-2.7.1.dev21/CLAUDE.md +1 -0
  3. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/PKG-INFO +1 -1
  4. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/mnamer/__version__.py +1 -1
  5. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/mnamer.egg-info/PKG-INFO +1 -1
  6. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/mnamer.egg-info/SOURCES.txt +2 -0
  7. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/.github/actions/init/action.yml +0 -0
  8. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/.github/actions/lint/action.yml +0 -0
  9. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/.github/actions/test/action.yml +0 -0
  10. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/.github/dependabot.yml +0 -0
  11. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/.github/workflows/publish.yml +0 -0
  12. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/.github/workflows/pull_request.yml +0 -0
  13. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/.github/workflows/push.yml +0 -0
  14. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/.gitignore +0 -0
  15. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/.python-version +0 -0
  16. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/.vscode/settings.json +0 -0
  17. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/Dockerfile +0 -0
  18. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/LICENSE.txt +0 -0
  19. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/MANIFEST.in +0 -0
  20. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/README.md +0 -0
  21. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/assets/design.eps +0 -0
  22. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/assets/logo-2.png +0 -0
  23. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/assets/logo-3.png +0 -0
  24. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/assets/logo.png +0 -0
  25. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/assets/recording.mov +0 -0
  26. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/assets/screenshot.eps +0 -0
  27. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/assets/screenshot.png +0 -0
  28. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/makefile +0 -0
  29. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/mnamer/__init__.py +0 -0
  30. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/mnamer/__main__.py +0 -0
  31. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/mnamer/argument.py +0 -0
  32. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/mnamer/const.py +0 -0
  33. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/mnamer/endpoints.py +0 -0
  34. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/mnamer/exceptions.py +0 -0
  35. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/mnamer/frontends.py +0 -0
  36. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/mnamer/language.py +0 -0
  37. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/mnamer/metadata.py +0 -0
  38. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/mnamer/providers.py +0 -0
  39. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/mnamer/py.typed +0 -0
  40. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/mnamer/setting_spec.py +0 -0
  41. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/mnamer/setting_store.py +0 -0
  42. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/mnamer/target.py +0 -0
  43. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/mnamer/tty.py +0 -0
  44. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/mnamer/types.py +0 -0
  45. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/mnamer/utils.py +0 -0
  46. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/mnamer.egg-info/dependency_links.txt +0 -0
  47. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/mnamer.egg-info/entry_points.txt +0 -0
  48. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/mnamer.egg-info/requires.txt +0 -0
  49. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/mnamer.egg-info/top_level.txt +0 -0
  50. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/pyproject.toml +0 -0
  51. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/pytest.ini +0 -0
  52. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/setup.cfg +0 -0
  53. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/__init__.py +0 -0
  54. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/conftest.py +0 -0
  55. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/e2e/__init__.py +0 -0
  56. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/e2e/conftest.py +0 -0
  57. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/e2e/test_directives.py +0 -0
  58. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/e2e/test_errors.py +0 -0
  59. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/e2e/test_moving.py +0 -0
  60. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/local/__init__.py +0 -0
  61. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/local/test_argument.py +0 -0
  62. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/local/test_language.py +0 -0
  63. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/local/test_metadata.py +0 -0
  64. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/local/test_setting_spec.py +0 -0
  65. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/local/test_setting_store.py +0 -0
  66. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/local/test_target.py +0 -0
  67. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/local/test_tty.py +0 -0
  68. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/local/test_utils.py +0 -0
  69. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/network/__init__.py +0 -0
  70. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/network/test_endpoints__omdb.py +0 -0
  71. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/network/test_endpoints__tmdb.py +0 -0
  72. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/network/test_endpoints__tvdb.py +0 -0
  73. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/network/test_endpoints__tvmaze.py +0 -0
  74. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/network/test_providers__omdb.py +0 -0
  75. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/network/test_providers__tmdb.py +0 -0
  76. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/network/test_providers__tvdb.py +0 -0
  77. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/tests/network/test_providers__tvmaze.py +0 -0
  78. {mnamer-2.7.1.dev19 → mnamer-2.7.1.dev21}/uv.lock +0 -0
@@ -0,0 +1,123 @@
1
+ # AGENTS.md
2
+
3
+ This file provides guidance to LLMs when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ 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.
8
+
9
+ ## Development Setup
10
+
11
+ This project requires Python 3.12+ and uses `uv` as the package manager:
12
+
13
+ ```bash
14
+ # Install dependencies
15
+ uv sync --dev
16
+
17
+ # Run mnamer locally
18
+ uv run mnamer [args]
19
+ ```
20
+
21
+ ## Common Commands
22
+
23
+ ### Testing
24
+
25
+ The test suite is organized by pytest markers. Keep local tests free of network
26
+ access; network and e2e tests can be flaky because they exercise providers and
27
+ the CLI workflow.
28
+
29
+ ```bash
30
+ # Run local unit tests (no network)
31
+ uv run pytest -m local
32
+
33
+ # Run network tests (requires internet, may be flaky)
34
+ uv run pytest -m network --reruns 3
35
+
36
+ # Run end-to-end tests
37
+ uv run pytest -m e2e --reruns 3
38
+
39
+ # Run all tests with coverage
40
+ uv run pytest --cov=./ --cov-report=term-missing
41
+
42
+ # Run a specific test file
43
+ uv run pytest tests/local/test_metadata.py
44
+
45
+ # Run a specific test function
46
+ uv run pytest tests/local/test_metadata.py::test_function_name
47
+ ```
48
+
49
+ Registered markers are declared in `pytest.ini`: `local`, `network`, `e2e`,
50
+ plus provider markers `omdb`, `tmdb`, `tvdb`, and `tvmaze`.
51
+
52
+ ### Linting and Formatting
53
+
54
+ ```bash
55
+ # Check code with ruff
56
+ uv run ruff check mnamer tests
57
+
58
+ # Format code with ruff
59
+ uv run ruff format mnamer tests
60
+
61
+ # Type check with mypy
62
+ uv run mypy mnamer tests
63
+ ```
64
+
65
+ ## Architecture
66
+
67
+ ### Core Data Flow
68
+
69
+ 1. **Entry Point** (`__main__.py:main`): Loads settings and initializes the CLI frontend
70
+ 2. **Frontend** (`frontends.py:Cli`): Orchestrates the file processing workflow
71
+ 3. **Target** (`target.py:Target`): Represents a media file, manages its metadata and relocation
72
+ 4. **Metadata** (`metadata.py`): Dataclasses for storing parsed and enriched metadata
73
+ - `MetadataMovie`: Movie-specific fields (name, year, id_imdb, id_tmdb)
74
+ - `MetadataEpisode`: TV episode fields (series, season, episode, id_tvdb, id_tvmaze)
75
+ 5. **Providers** (`providers.py`): High-level interface for querying metadata APIs
76
+ - `Tmdb`, `Omdb`: Movie providers
77
+ - `Tvdb`, `Tvmaze`: TV episode providers
78
+ 6. **Endpoints** (`endpoints.py`): Low-level API request functions and response TypedDicts
79
+
80
+ ### Key Architectural Patterns
81
+
82
+ - **Metadata Parsing**: Uses `guessit` library to extract metadata from filenames. The `Target._parse()` method converts guessit output into `Metadata` objects.
83
+
84
+ - **Target Discovery**: `Target.populate_paths()` crawls positional targets, applies `--recurse`, `--ignore`, `--mask`, de-duplicates paths, and filters by `--media` when supplied.
85
+
86
+ - **Provider System**: Providers are registered per-provider in a class variable cache (`Target._providers`). `Provider.provider_factory()` instantiates providers based on the `ProviderType` enum and settings.
87
+
88
+ - **Settings Management**: `SettingStore` loads 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.
89
+
90
+ - **Template Formatting**: `MetadataMovie.__format__()` and `MetadataEpisode.__format__()` use `_MetaFormatter` to substitute template variables like `{name}`, `{season:02}`, and `{extension}`. Templates are configured per media type via settings.
91
+
92
+ - **File Relocation**: `Target.destination` combines the optional `movie_directory` or `episode_directory` setting with the matching format template, then applies replacement, scene/lowercase settings, and filename sanitization. `Target.relocate()` creates destination directories and moves the file.
93
+
94
+ ### Important Implementation Details
95
+
96
+ - **Subtitle Handling**: Subtitle files (`.srt`, `.idx`, `.sub`) are detected via `is_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`).
97
+
98
+ - **Language Support**: The `Language` class (in `language.py`) wraps babelfish for language code conversion. Providers return metadata in the language specified by `--language` setting.
99
+
100
+ - **Request Caching**: API requests go through `utils.get_session()`, a `requests-cache` `CachedSession` stored under the user cache directory for six days. Cache can be cleared with the `--clear-cache` directive or bypassed per run with `--no-cache`.
101
+
102
+ - **Error Handling**: Custom exceptions in `exceptions.py` distinguish between network errors (`MnamerNetworkException`), missing results (`MnamerNotFoundException`), and user actions (`MnamerSkipException`, `MnamerAbortException`).
103
+
104
+ - **Test Organization**:
105
+ - `tests/local/`: Pure unit tests, no network or filesystem side effects
106
+ - `tests/network/`: Integration tests hitting real APIs (may be flaky)
107
+ - `tests/e2e/`: End-to-end tests with actual file operations
108
+
109
+ ## Configuration
110
+
111
+ - **Config File**: `.mnamer-v2.json` in the current or parent directories, or the explicit path from `--config-path`
112
+ - **Settings Precedence**: CLI arguments > config file > defaults
113
+ - **Directives vs Parameters**: Directives (like `--test`, `--id-tmdb`) are one-time overrides not stored in config files
114
+ - **Config-only Fields**: `api_key_omdb`, `api_key_tmdb`, `api_key_tvdb`, `api_key_tvmaze`, `replace_before`, and `replace_after` are only loaded from config/defaults, not CLI flags
115
+
116
+ ## API Keys
117
+
118
+ Providers load API keys through `Provider.from_settings()` using config-only
119
+ fields named `api_key_<provider>`. If unset, provider classes fall back to
120
+ environment variables (`API_KEY_OMDB`, `API_KEY_TMDB`, `API_KEY_TVDB`,
121
+ `API_KEY_TVMAZE`) and then bundled defaults where present.
122
+
123
+ Check provider classes in `providers.py` for API key handling via `from_settings()` class method.
@@ -0,0 +1 @@
1
+ @AGENTS.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mnamer
3
- Version: 2.7.1.dev19
3
+ Version: 2.7.1.dev21
4
4
  Summary: A command-line utility for organizing media files.
5
5
  Author-email: Jessy Williams <jessy@jessywilliams.com>
6
6
  Maintainer-email: Jessy Williams <jessy@jessywilliams.com>
@@ -1,4 +1,4 @@
1
1
  # file generated by setuptools_scm
2
2
  # don't change, don't track in version control
3
3
 
4
- __version__ = "2.7.1.dev19"
4
+ __version__ = "2.7.1.dev21"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mnamer
3
- Version: 2.7.1.dev19
3
+ Version: 2.7.1.dev21
4
4
  Summary: A command-line utility for organizing media files.
5
5
  Author-email: Jessy Williams <jessy@jessywilliams.com>
6
6
  Maintainer-email: Jessy Williams <jessy@jessywilliams.com>
@@ -1,5 +1,7 @@
1
1
  .gitignore
2
2
  .python-version
3
+ AGENTS.md
4
+ CLAUDE.md
3
5
  Dockerfile
4
6
  LICENSE.txt
5
7
  MANIFEST.in
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes