mkv-episode-matcher 0.9.3__tar.gz → 0.9.5__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.

Potentially problematic release.


This version of mkv-episode-matcher might be problematic. Click here for more details.

Files changed (48) hide show
  1. mkv_episode_matcher-0.9.5/.github/workflows/claude-code-review.yml +78 -0
  2. mkv_episode_matcher-0.9.5/.github/workflows/claude.yml +64 -0
  3. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/CHANGELOG.md +12 -0
  4. mkv_episode_matcher-0.9.5/LICENSE +21 -0
  5. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/PKG-INFO +25 -2
  6. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/mkv_episode_matcher/config.py +4 -4
  7. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/mkv_episode_matcher.egg-info/PKG-INFO +25 -2
  8. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/mkv_episode_matcher.egg-info/SOURCES.txt +4 -0
  9. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/mkv_episode_matcher.egg-info/requires.txt +1 -0
  10. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/pyproject.toml +2 -2
  11. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/setup.cfg +1 -1
  12. mkv_episode_matcher-0.9.5/tests/test_config_special_characters.py +174 -0
  13. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/uv.lock +1174 -1174
  14. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/.coverage +0 -0
  15. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/.gitattributes +0 -0
  16. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/.github/funding.yml +0 -0
  17. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/.github/workflows/documentation.yml +0 -0
  18. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/.github/workflows/python-publish.yml +0 -0
  19. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/.github/workflows/tests.yml +0 -0
  20. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/.gitignore +0 -0
  21. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/.gitmodules +0 -0
  22. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/.python-version +0 -0
  23. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/.vscode/settings.json +0 -0
  24. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/README.md +0 -0
  25. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/docs/api/index.md +0 -0
  26. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/docs/changelog.md +0 -0
  27. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/docs/cli.md +0 -0
  28. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/docs/configuration.md +0 -0
  29. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/docs/installation.md +0 -0
  30. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/docs/quickstart.md +0 -0
  31. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/docs/tips.md +0 -0
  32. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/mkdocs.yml +0 -0
  33. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/mkv_episode_matcher/.gitattributes +0 -0
  34. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/mkv_episode_matcher/__init__.py +0 -0
  35. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/mkv_episode_matcher/__main__.py +0 -0
  36. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/mkv_episode_matcher/episode_identification.py +0 -0
  37. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/mkv_episode_matcher/episode_matcher.py +0 -0
  38. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/mkv_episode_matcher/subtitle_utils.py +0 -0
  39. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/mkv_episode_matcher/tmdb_client.py +0 -0
  40. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/mkv_episode_matcher/utils.py +0 -0
  41. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/mkv_episode_matcher.egg-info/dependency_links.txt +0 -0
  42. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/mkv_episode_matcher.egg-info/entry_points.txt +0 -0
  43. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/mkv_episode_matcher.egg-info/top_level.txt +0 -0
  44. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/setup.py +0 -0
  45. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/tests/__init__.py +0 -0
  46. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/tests/test_main.py +0 -0
  47. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/tests/test_path_handling.py +0 -0
  48. {mkv_episode_matcher-0.9.3 → mkv_episode_matcher-0.9.5}/tests/test_trailing_slash.py +0 -0
@@ -0,0 +1,78 @@
1
+ name: Claude Code Review
2
+
3
+ on:
4
+ pull_request:
5
+ types: [opened, synchronize]
6
+ # Optional: Only run on specific file changes
7
+ # paths:
8
+ # - "src/**/*.ts"
9
+ # - "src/**/*.tsx"
10
+ # - "src/**/*.js"
11
+ # - "src/**/*.jsx"
12
+
13
+ jobs:
14
+ claude-review:
15
+ # Optional: Filter by PR author
16
+ # if: |
17
+ # github.event.pull_request.user.login == 'external-contributor' ||
18
+ # github.event.pull_request.user.login == 'new-developer' ||
19
+ # github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
20
+
21
+ runs-on: ubuntu-latest
22
+ permissions:
23
+ contents: read
24
+ pull-requests: read
25
+ issues: read
26
+ id-token: write
27
+
28
+ steps:
29
+ - name: Checkout repository
30
+ uses: actions/checkout@v4
31
+ with:
32
+ fetch-depth: 1
33
+
34
+ - name: Run Claude Code Review
35
+ id: claude-review
36
+ uses: anthropics/claude-code-action@beta
37
+ with:
38
+ claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
39
+
40
+ # Optional: Specify model (defaults to Claude Sonnet 4, uncomment for Claude Opus 4)
41
+ # model: "claude-opus-4-20250514"
42
+
43
+ # Direct prompt for automated review (no @claude mention needed)
44
+ direct_prompt: |
45
+ Please review this pull request and provide feedback on:
46
+ - Code quality and best practices
47
+ - Potential bugs or issues
48
+ - Performance considerations
49
+ - Security concerns
50
+ - Test coverage
51
+
52
+ Be constructive and helpful in your feedback.
53
+
54
+ # Optional: Use sticky comments to make Claude reuse the same comment on subsequent pushes to the same PR
55
+ # use_sticky_comment: true
56
+
57
+ # Optional: Customize review based on file types
58
+ # direct_prompt: |
59
+ # Review this PR focusing on:
60
+ # - For TypeScript files: Type safety and proper interface usage
61
+ # - For API endpoints: Security, input validation, and error handling
62
+ # - For React components: Performance, accessibility, and best practices
63
+ # - For tests: Coverage, edge cases, and test quality
64
+
65
+ # Optional: Different prompts for different authors
66
+ # direct_prompt: |
67
+ # ${{ github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' &&
68
+ # 'Welcome! Please review this PR from a first-time contributor. Be encouraging and provide detailed explanations for any suggestions.' ||
69
+ # 'Please provide a thorough code review focusing on our coding standards and best practices.' }}
70
+
71
+ # Optional: Add specific tools for running tests or linting
72
+ # allowed_tools: "Bash(npm run test),Bash(npm run lint),Bash(npm run typecheck)"
73
+
74
+ # Optional: Skip review for certain conditions
75
+ # if: |
76
+ # !contains(github.event.pull_request.title, '[skip-review]') &&
77
+ # !contains(github.event.pull_request.title, '[WIP]')
78
+
@@ -0,0 +1,64 @@
1
+ name: Claude Code
2
+
3
+ on:
4
+ issue_comment:
5
+ types: [created]
6
+ pull_request_review_comment:
7
+ types: [created]
8
+ issues:
9
+ types: [opened, assigned]
10
+ pull_request_review:
11
+ types: [submitted]
12
+
13
+ jobs:
14
+ claude:
15
+ if: |
16
+ (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
17
+ (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
18
+ (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
19
+ (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
20
+ runs-on: ubuntu-latest
21
+ permissions:
22
+ contents: read
23
+ pull-requests: read
24
+ issues: read
25
+ id-token: write
26
+ actions: read # Required for Claude to read CI results on PRs
27
+ steps:
28
+ - name: Checkout repository
29
+ uses: actions/checkout@v4
30
+ with:
31
+ fetch-depth: 1
32
+
33
+ - name: Run Claude Code
34
+ id: claude
35
+ uses: anthropics/claude-code-action@beta
36
+ with:
37
+ claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
38
+
39
+ # This is an optional setting that allows Claude to read CI results on PRs
40
+ additional_permissions: |
41
+ actions: read
42
+
43
+ # Optional: Specify model (defaults to Claude Sonnet 4, uncomment for Claude Opus 4)
44
+ # model: "claude-opus-4-20250514"
45
+
46
+ # Optional: Customize the trigger phrase (default: @claude)
47
+ # trigger_phrase: "/claude"
48
+
49
+ # Optional: Trigger when specific user is assigned to an issue
50
+ # assignee_trigger: "claude-bot"
51
+
52
+ # Optional: Allow Claude to run specific commands
53
+ # allowed_tools: "Bash(npm install),Bash(npm run build),Bash(npm run test:*),Bash(npm run lint:*)"
54
+
55
+ # Optional: Add custom instructions for Claude to customize its behavior for your project
56
+ # custom_instructions: |
57
+ # Follow our coding standards
58
+ # Ensure all new code has tests
59
+ # Use TypeScript for new files
60
+
61
+ # Optional: Custom environment variables for Claude
62
+ # claude_env: |
63
+ # NODE_ENV: test
64
+
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.9.3] - 2025-07-07
9
+
10
+ ### Added
11
+ - Onboarding flag (`--onboard`) and interactive onboarding sequence for first-time setup and configuration updates
12
+ - Onboarding prompts for TMDb API key, OpenSubtitles API key, Consumer Name, Username, Password, and Show Directory
13
+ - Existing config values are shown as defaults during onboarding and can be accepted or overwritten
14
+ - Documentation updated to reflect onboarding requirements and workflow in README and docs
15
+
16
+ ### Changed
17
+ - Improved configuration experience for new and returning users
18
+ - Quick Start and Configuration documentation now reference onboarding and required credentials
19
+
8
20
  ## [0.9.0] - 2025-06-01
9
21
 
10
22
  ### Changed
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Jonathan Sakkos
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,11 +1,31 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkv-episode-matcher
3
- Version: 0.9.3
3
+ Version: 0.9.5
4
4
  Summary: The MKV Episode Matcher is a tool for identifying TV series episodes from MKV files and renaming the files accordingly.
5
5
  Home-page: https://github.com/Jsakkos/mkv-episode-matcher
6
6
  Author: Jonathan Sakkos
7
7
  Author-email: Jsakkos <jonathansakkos@gmail.com>
8
- License: MIT
8
+ License: MIT License
9
+
10
+ Copyright (c) 2025 Jonathan Sakkos
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in all
20
+ copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ SOFTWARE.
9
29
  Project-URL: Documentation, https://github.com/Jsakkos/mkv-episode-matcher#readme
10
30
  Project-URL: Issues, https://github.com/Jsakkos/mkv-episode-matcher/issues
11
31
  Project-URL: Source, https://github.com/Jsakkos/mkv-episode-matcher
@@ -16,6 +36,8 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
16
36
  Classifier: Programming Language :: Python :: Implementation :: PyPy
17
37
  Requires-Python: <3.13,>=3.9
18
38
  Description-Content-Type: text/markdown
39
+ License-File: LICENSE
40
+ Requires-Dist: chardet>=5.2.0
19
41
  Requires-Dist: configparser>=7.1.0
20
42
  Requires-Dist: ffmpeg>=1.4
21
43
  Requires-Dist: loguru>=0.7.2
@@ -28,6 +50,7 @@ Requires-Dist: tmdb-client>=0.0.1
28
50
  Requires-Dist: torch>=2.5.1
29
51
  Requires-Dist: torchaudio>=2.5.1
30
52
  Requires-Dist: torchvision>=0.20.1
53
+ Dynamic: license-file
31
54
 
32
55
  # MKV Episode Matcher
33
56
 
@@ -43,7 +43,7 @@ def set_config(
43
43
  Returns:
44
44
  None
45
45
  """
46
- config = configparser.ConfigParser()
46
+ config = configparser.ConfigParser(interpolation=None)
47
47
  config["Config"] = {
48
48
  "tmdb_api_key": str(tmdb_api_key),
49
49
  "show_dir": show_dir,
@@ -56,7 +56,7 @@ def set_config(
56
56
  logger.info(
57
57
  f"Setting config with API:{tmdb_api_key}, show_dir: {show_dir}, and max_threads: {MAX_THREADS}"
58
58
  )
59
- with open(file, "w") as configfile:
59
+ with open(file, "w", encoding="utf-8") as configfile:
60
60
  config.write(configfile)
61
61
 
62
62
 
@@ -72,8 +72,8 @@ def get_config(file):
72
72
 
73
73
  """
74
74
  logger.info(f"Loading config from {file}")
75
- config = configparser.ConfigParser()
75
+ config = configparser.ConfigParser(interpolation=None)
76
76
  if Path(file).exists():
77
- config.read(file)
77
+ config.read(file, encoding="utf-8")
78
78
  return config["Config"] if "Config" in config else None
79
79
  return {}
@@ -1,11 +1,31 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkv-episode-matcher
3
- Version: 0.9.3
3
+ Version: 0.9.5
4
4
  Summary: The MKV Episode Matcher is a tool for identifying TV series episodes from MKV files and renaming the files accordingly.
5
5
  Home-page: https://github.com/Jsakkos/mkv-episode-matcher
6
6
  Author: Jonathan Sakkos
7
7
  Author-email: Jsakkos <jonathansakkos@gmail.com>
8
- License: MIT
8
+ License: MIT License
9
+
10
+ Copyright (c) 2025 Jonathan Sakkos
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in all
20
+ copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ SOFTWARE.
9
29
  Project-URL: Documentation, https://github.com/Jsakkos/mkv-episode-matcher#readme
10
30
  Project-URL: Issues, https://github.com/Jsakkos/mkv-episode-matcher/issues
11
31
  Project-URL: Source, https://github.com/Jsakkos/mkv-episode-matcher
@@ -16,6 +36,8 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
16
36
  Classifier: Programming Language :: Python :: Implementation :: PyPy
17
37
  Requires-Python: <3.13,>=3.9
18
38
  Description-Content-Type: text/markdown
39
+ License-File: LICENSE
40
+ Requires-Dist: chardet>=5.2.0
19
41
  Requires-Dist: configparser>=7.1.0
20
42
  Requires-Dist: ffmpeg>=1.4
21
43
  Requires-Dist: loguru>=0.7.2
@@ -28,6 +50,7 @@ Requires-Dist: tmdb-client>=0.0.1
28
50
  Requires-Dist: torch>=2.5.1
29
51
  Requires-Dist: torchaudio>=2.5.1
30
52
  Requires-Dist: torchvision>=0.20.1
53
+ Dynamic: license-file
31
54
 
32
55
  # MKV Episode Matcher
33
56
 
@@ -4,6 +4,7 @@
4
4
  .gitmodules
5
5
  .python-version
6
6
  CHANGELOG.md
7
+ LICENSE
7
8
  README.md
8
9
  mkdocs.yml
9
10
  pyproject.toml
@@ -11,6 +12,8 @@ setup.cfg
11
12
  setup.py
12
13
  uv.lock
13
14
  .github/funding.yml
15
+ .github/workflows/claude-code-review.yml
16
+ .github/workflows/claude.yml
14
17
  .github/workflows/documentation.yml
15
18
  .github/workflows/python-publish.yml
16
19
  .github/workflows/tests.yml
@@ -38,6 +41,7 @@ mkv_episode_matcher.egg-info/entry_points.txt
38
41
  mkv_episode_matcher.egg-info/requires.txt
39
42
  mkv_episode_matcher.egg-info/top_level.txt
40
43
  tests/__init__.py
44
+ tests/test_config_special_characters.py
41
45
  tests/test_main.py
42
46
  tests/test_path_handling.py
43
47
  tests/test_trailing_slash.py
@@ -1,3 +1,4 @@
1
+ chardet>=5.2.0
1
2
  configparser>=7.1.0
2
3
  ffmpeg>=1.4
3
4
  loguru>=0.7.2
@@ -10,7 +10,7 @@ name = "mkv-episode-matcher"
10
10
  description = "The MKV Episode Matcher is a tool for identifying TV series episodes from MKV files and renaming the files accordingly."
11
11
  dynamic = ["version"]
12
12
  readme = "README.md"
13
- license = { text = "MIT" }
13
+ license = { file = "LICENSE" }
14
14
  classifiers = [
15
15
  "Development Status :: 4 - Beta",
16
16
  "Programming Language :: Python",
@@ -23,6 +23,7 @@ authors = [
23
23
  ]
24
24
  requires-python = ">=3.9,<3.13"
25
25
  dependencies = [
26
+ "chardet>=5.2.0",
26
27
  "configparser>=7.1.0",
27
28
  "ffmpeg>=1.4",
28
29
  "loguru>=0.7.2",
@@ -49,7 +50,6 @@ dev = [
49
50
  "pytest-cov>=6.0.0",
50
51
  "pytest>=8.3.3",
51
52
  "ruff>=0.8.0",
52
- "chardet>=5.2.0",
53
53
  "matplotlib>=3.9.4",
54
54
  "memory-profiler>=0.61.0",
55
55
  "line-profiler>=4.2.0",
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = mkv_episode_matcher
3
- version = 0.9.3
3
+ version = 0.9.5
4
4
  author = Jonathan Sakkos
5
5
  author_email = jonathansakkos@gmail.com
6
6
  description = The MKV Episode Matcher is a tool for identifying TV series episodes from MKV files and renaming the files accordingly.
@@ -0,0 +1,174 @@
1
+ """Test cases for config.py handling of special characters in passwords."""
2
+
3
+ import tempfile
4
+ from pathlib import Path
5
+
6
+ import pytest
7
+
8
+ from mkv_episode_matcher.config import get_config, set_config
9
+
10
+
11
+ class TestConfigSpecialCharacters:
12
+ """Test that config handling works correctly with special characters in passwords."""
13
+
14
+ @pytest.fixture
15
+ def temp_config_file(self):
16
+ """Create a temporary config file for testing."""
17
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.ini', delete=False) as f:
18
+ yield f.name
19
+ Path(f.name).unlink(missing_ok=True)
20
+
21
+ @pytest.fixture
22
+ def mock_config_data(self):
23
+ """Mock configuration data for testing."""
24
+ return {
25
+ "tmdb_api_key": "test_tmdb_api_key",
26
+ "open_subtitles_api_key": "test_os_api_key",
27
+ "open_subtitles_user_agent": "test_user_agent",
28
+ "open_subtitles_username": "test_username",
29
+ "show_dir": "/test/path"
30
+ }
31
+
32
+ def test_password_with_percent_symbol(self, temp_config_file, mock_config_data):
33
+ """Test that passwords containing % symbol don't cause interpolation errors."""
34
+ password_with_percent = "H7z*X$X29JdJ^#%Q" # gitguardian:ignore
35
+
36
+ # This should not raise a ValueError
37
+ set_config(
38
+ mock_config_data["tmdb_api_key"],
39
+ mock_config_data["open_subtitles_api_key"],
40
+ mock_config_data["open_subtitles_user_agent"],
41
+ mock_config_data["open_subtitles_username"],
42
+ password_with_percent,
43
+ mock_config_data["show_dir"],
44
+ temp_config_file,
45
+ )
46
+
47
+ # Verify the config was written successfully
48
+ config = get_config(temp_config_file)
49
+ assert config is not None
50
+ assert config["open_subtitles_password"] == password_with_percent
51
+
52
+ def test_password_with_multiple_percent_symbols(self, temp_config_file, mock_config_data):
53
+ """Test that passwords with multiple % symbols work correctly."""
54
+ password_with_percents = "password%with%multiple%percent%signs"
55
+
56
+ set_config(
57
+ mock_config_data["tmdb_api_key"],
58
+ mock_config_data["open_subtitles_api_key"],
59
+ mock_config_data["open_subtitles_user_agent"],
60
+ mock_config_data["open_subtitles_username"],
61
+ password_with_percents,
62
+ mock_config_data["show_dir"],
63
+ temp_config_file,
64
+ )
65
+
66
+ config = get_config(temp_config_file)
67
+ assert config["open_subtitles_password"] == password_with_percents
68
+
69
+ def test_password_with_interpolation_like_syntax(self, temp_config_file, mock_config_data):
70
+ """Test that passwords resembling interpolation syntax are handled correctly."""
71
+ # This resembles ConfigParser interpolation syntax but should be treated literally
72
+ password_with_interpolation = "%(section)s_password_%(option)s"
73
+
74
+ set_config(
75
+ mock_config_data["tmdb_api_key"],
76
+ mock_config_data["open_subtitles_api_key"],
77
+ mock_config_data["open_subtitles_user_agent"],
78
+ mock_config_data["open_subtitles_username"],
79
+ password_with_interpolation,
80
+ mock_config_data["show_dir"],
81
+ temp_config_file,
82
+ )
83
+
84
+ config = get_config(temp_config_file)
85
+ assert config["open_subtitles_password"] == password_with_interpolation
86
+
87
+ def test_password_with_various_special_characters(self, temp_config_file, mock_config_data):
88
+ """Test that passwords with various special characters work correctly."""
89
+ special_passwords = [
90
+ "pass@word!123",
91
+ "my$ecret#key*",
92
+ "complex&password^with()brackets[]",
93
+ "unicode_测试_password",
94
+ "spaces in password",
95
+ "tabs\tand\nnewlines",
96
+ ]
97
+
98
+ for password in special_passwords:
99
+ set_config(
100
+ mock_config_data["tmdb_api_key"],
101
+ mock_config_data["open_subtitles_api_key"],
102
+ mock_config_data["open_subtitles_user_agent"],
103
+ mock_config_data["open_subtitles_username"],
104
+ password,
105
+ mock_config_data["show_dir"],
106
+ temp_config_file,
107
+ )
108
+
109
+ config = get_config(temp_config_file)
110
+ assert config["open_subtitles_password"] == password, f"Failed for password: {password}"
111
+
112
+ def test_original_bug_case(self, temp_config_file, mock_config_data):
113
+ """Test the specific password from the original bug report."""
114
+ # This is the exact password that caused the original issue
115
+ problematic_password = "H7z*X$X29JdJ^#%Q" # gitguardian:ignore
116
+
117
+ # Before the fix, this would raise:
118
+ # ValueError: invalid interpolation syntax in 'H7z*X$X29JdJ^#%Q' at position 14
119
+ set_config(
120
+ mock_config_data["tmdb_api_key"],
121
+ mock_config_data["open_subtitles_api_key"],
122
+ mock_config_data["open_subtitles_user_agent"],
123
+ mock_config_data["open_subtitles_username"],
124
+ problematic_password,
125
+ mock_config_data["show_dir"],
126
+ temp_config_file,
127
+ )
128
+
129
+ # Verify we can read it back correctly
130
+ config = get_config(temp_config_file)
131
+ assert config is not None
132
+ assert config["open_subtitles_password"] == problematic_password
133
+
134
+ # Verify all other fields are preserved
135
+ assert config["tmdb_api_key"] == mock_config_data["tmdb_api_key"]
136
+ assert config["open_subtitles_username"] == mock_config_data["open_subtitles_username"]
137
+ assert config["show_dir"] == mock_config_data["show_dir"]
138
+
139
+ def test_empty_password(self, temp_config_file, mock_config_data):
140
+ """Test that empty passwords are handled correctly."""
141
+ empty_password = ""
142
+
143
+ set_config(
144
+ mock_config_data["tmdb_api_key"],
145
+ mock_config_data["open_subtitles_api_key"],
146
+ mock_config_data["open_subtitles_user_agent"],
147
+ mock_config_data["open_subtitles_username"],
148
+ empty_password,
149
+ mock_config_data["show_dir"],
150
+ temp_config_file,
151
+ )
152
+
153
+ config = get_config(temp_config_file)
154
+ assert config["open_subtitles_password"] == empty_password
155
+
156
+ def test_config_persistence(self, temp_config_file, mock_config_data):
157
+ """Test that config values persist correctly across multiple operations."""
158
+ password = "persistent%password#123"
159
+
160
+ # Set config
161
+ set_config(
162
+ mock_config_data["tmdb_api_key"],
163
+ mock_config_data["open_subtitles_api_key"],
164
+ mock_config_data["open_subtitles_user_agent"],
165
+ mock_config_data["open_subtitles_username"],
166
+ password,
167
+ mock_config_data["show_dir"],
168
+ temp_config_file,
169
+ )
170
+
171
+ # Read config multiple times to ensure consistency
172
+ for _ in range(3):
173
+ config = get_config(temp_config_file)
174
+ assert config["open_subtitles_password"] == password