mkv-episode-matcher 0.9.5__tar.gz → 0.9.6__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 (50) hide show
  1. mkv_episode_matcher-0.9.6/.claude/settings.local.json +20 -0
  2. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/.coverage +0 -0
  3. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/PKG-INFO +1 -1
  4. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/mkv_episode_matcher/episode_identification.py +5 -3
  5. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/mkv_episode_matcher.egg-info/PKG-INFO +1 -1
  6. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/mkv_episode_matcher.egg-info/SOURCES.txt +2 -0
  7. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/setup.cfg +1 -1
  8. mkv_episode_matcher-0.9.6/tests/test_episode_identification.py +286 -0
  9. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/.gitattributes +0 -0
  10. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/.github/funding.yml +0 -0
  11. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/.github/workflows/claude-code-review.yml +0 -0
  12. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/.github/workflows/claude.yml +0 -0
  13. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/.github/workflows/documentation.yml +0 -0
  14. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/.github/workflows/python-publish.yml +0 -0
  15. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/.github/workflows/tests.yml +0 -0
  16. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/.gitignore +0 -0
  17. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/.gitmodules +0 -0
  18. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/.python-version +0 -0
  19. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/.vscode/settings.json +0 -0
  20. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/CHANGELOG.md +0 -0
  21. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/LICENSE +0 -0
  22. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/README.md +0 -0
  23. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/docs/api/index.md +0 -0
  24. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/docs/changelog.md +0 -0
  25. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/docs/cli.md +0 -0
  26. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/docs/configuration.md +0 -0
  27. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/docs/installation.md +0 -0
  28. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/docs/quickstart.md +0 -0
  29. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/docs/tips.md +0 -0
  30. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/mkdocs.yml +0 -0
  31. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/mkv_episode_matcher/.gitattributes +0 -0
  32. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/mkv_episode_matcher/__init__.py +0 -0
  33. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/mkv_episode_matcher/__main__.py +0 -0
  34. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/mkv_episode_matcher/config.py +0 -0
  35. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/mkv_episode_matcher/episode_matcher.py +0 -0
  36. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/mkv_episode_matcher/subtitle_utils.py +0 -0
  37. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/mkv_episode_matcher/tmdb_client.py +0 -0
  38. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/mkv_episode_matcher/utils.py +0 -0
  39. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/mkv_episode_matcher.egg-info/dependency_links.txt +0 -0
  40. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/mkv_episode_matcher.egg-info/entry_points.txt +0 -0
  41. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/mkv_episode_matcher.egg-info/requires.txt +0 -0
  42. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/mkv_episode_matcher.egg-info/top_level.txt +0 -0
  43. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/pyproject.toml +0 -0
  44. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/setup.py +0 -0
  45. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/tests/__init__.py +0 -0
  46. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/tests/test_config_special_characters.py +0 -0
  47. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/tests/test_main.py +0 -0
  48. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/tests/test_path_handling.py +0 -0
  49. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/tests/test_trailing_slash.py +0 -0
  50. {mkv_episode_matcher-0.9.5 → mkv_episode_matcher-0.9.6}/uv.lock +0 -0
@@ -0,0 +1,20 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(gh issue view:*)",
5
+ "Bash(git checkout:*)",
6
+ "Bash(python -m pytest tests/ -v)",
7
+ "Bash(uv run pytest:*)",
8
+ "Bash(gh pr view:*)",
9
+ "Bash(gh pr checks:*)",
10
+ "WebFetch(domain:github.com)",
11
+ "Bash(gh run view:*)",
12
+ "Bash(uv run:*)",
13
+ "Bash(git add:*)",
14
+ "Bash(git commit:*)",
15
+ "Bash(git push:*)",
16
+ "Bash(rm:*)"
17
+ ],
18
+ "deny": []
19
+ }
20
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkv-episode-matcher
3
- Version: 0.9.5
3
+ Version: 0.9.6
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
@@ -155,11 +155,13 @@ class EpisodeMatcher:
155
155
  ]
156
156
 
157
157
  reference_files = []
158
- for _pattern in patterns:
158
+ for pattern in patterns:
159
+ # Use case-insensitive file extension matching by checking both .srt and .SRT
160
+ srt_files = list(reference_dir.glob("*.srt")) + list(reference_dir.glob("*.SRT"))
159
161
  files = [
160
162
  f
161
- for f in reference_dir.glob("*.srt")
162
- if any(re.search(f"{p}\\d+", f.name, re.IGNORECASE) for p in patterns)
163
+ for f in srt_files
164
+ if re.search(f"{pattern}\\d+", f.name, re.IGNORECASE)
163
165
  ]
164
166
  reference_files.extend(files)
165
167
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkv-episode-matcher
3
- Version: 0.9.5
3
+ Version: 0.9.6
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
@@ -11,6 +11,7 @@ pyproject.toml
11
11
  setup.cfg
12
12
  setup.py
13
13
  uv.lock
14
+ .claude/settings.local.json
14
15
  .github/funding.yml
15
16
  .github/workflows/claude-code-review.yml
16
17
  .github/workflows/claude.yml
@@ -42,6 +43,7 @@ mkv_episode_matcher.egg-info/requires.txt
42
43
  mkv_episode_matcher.egg-info/top_level.txt
43
44
  tests/__init__.py
44
45
  tests/test_config_special_characters.py
46
+ tests/test_episode_identification.py
45
47
  tests/test_main.py
46
48
  tests/test_path_handling.py
47
49
  tests/test_trailing_slash.py
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = mkv_episode_matcher
3
- version = 0.9.5
3
+ version = 0.9.6
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,286 @@
1
+ import unittest
2
+ import tempfile
3
+ import re
4
+ from pathlib import Path
5
+ from unittest.mock import Mock, patch
6
+
7
+ from mkv_episode_matcher.episode_identification import EpisodeMatcher
8
+
9
+
10
+ class TestEpisodeIdentificationPatternMatching(unittest.TestCase):
11
+ """Test the pattern matching functionality in episode identification."""
12
+
13
+ def setUp(self):
14
+ """Set up test fixtures."""
15
+ self.temp_dir = tempfile.TemporaryDirectory()
16
+ self.cache_dir = Path(self.temp_dir.name)
17
+
18
+ # Mock subtitle cache
19
+ self.mock_subtitle_cache = Mock()
20
+
21
+ # Create identifier instance
22
+ self.identifier = EpisodeMatcher(
23
+ cache_dir=self.cache_dir,
24
+ show_name="Test Show (2023)"
25
+ )
26
+
27
+ def tearDown(self):
28
+ """Clean up test fixtures."""
29
+ self.temp_dir.cleanup()
30
+
31
+ def _create_test_files(self, filenames, show_name=None):
32
+ """Create test subtitle files in the cache directory."""
33
+ if show_name is None:
34
+ show_name = self.identifier.show_name
35
+
36
+ show_dir = self.cache_dir / "data" / show_name
37
+ show_dir.mkdir(parents=True, exist_ok=True)
38
+
39
+ created_files = []
40
+ for filename in filenames:
41
+ file_path = show_dir / filename
42
+ file_path.write_text("dummy subtitle content")
43
+ created_files.append(file_path)
44
+
45
+ return created_files
46
+
47
+ def test_pattern_matching_s01e_format(self):
48
+ """Test pattern matching for S01E format files."""
49
+ # Create test files with S01E format
50
+ test_files = [
51
+ "Test Show (2023) - S01E01.srt",
52
+ "Test Show (2023) - S01E02.srt",
53
+ "Test Show (2023) - S01E10.srt",
54
+ "Test Show (2023) - S02E01.srt", # Should not match season 1
55
+ ]
56
+
57
+ self._create_test_files(test_files)
58
+
59
+ # Test season 1 matching
60
+ reference_files = self.identifier.get_reference_files(1)
61
+
62
+ # Should find 3 files for season 1
63
+ self.assertEqual(len(reference_files), 3)
64
+
65
+ # Verify correct files are matched
66
+ matched_names = [f.name for f in reference_files]
67
+ self.assertIn("Test Show (2023) - S01E01.srt", matched_names)
68
+ self.assertIn("Test Show (2023) - S01E02.srt", matched_names)
69
+ self.assertIn("Test Show (2023) - S01E10.srt", matched_names)
70
+ self.assertNotIn("Test Show (2023) - S02E01.srt", matched_names)
71
+
72
+ def test_pattern_matching_s1e_format(self):
73
+ """Test pattern matching for S1E format files."""
74
+ test_files = [
75
+ "Show - S1E01.srt",
76
+ "Show - S1E05.srt",
77
+ "Show - S2E01.srt", # Should not match season 1
78
+ ]
79
+
80
+ self._create_test_files(test_files)
81
+
82
+ reference_files = self.identifier.get_reference_files(1)
83
+
84
+ # Should find 2 files for season 1
85
+ self.assertEqual(len(reference_files), 2)
86
+
87
+ matched_names = [f.name for f in reference_files]
88
+ self.assertIn("Show - S1E01.srt", matched_names)
89
+ self.assertIn("Show - S1E05.srt", matched_names)
90
+ self.assertNotIn("Show - S2E01.srt", matched_names)
91
+
92
+ def test_pattern_matching_01x_format(self):
93
+ """Test pattern matching for 01x format files."""
94
+ test_files = [
95
+ "Show - 01x01.srt",
96
+ "Show - 01x15.srt",
97
+ "Show - 02x01.srt", # Should not match season 1
98
+ ]
99
+
100
+ self._create_test_files(test_files)
101
+
102
+ reference_files = self.identifier.get_reference_files(1)
103
+
104
+ # Should find 2 files for season 1
105
+ self.assertEqual(len(reference_files), 2)
106
+
107
+ matched_names = [f.name for f in reference_files]
108
+ self.assertIn("Show - 01x01.srt", matched_names)
109
+ self.assertIn("Show - 01x15.srt", matched_names)
110
+ self.assertNotIn("Show - 02x01.srt", matched_names)
111
+
112
+ def test_pattern_matching_1x_format(self):
113
+ """Test pattern matching for 1x format files."""
114
+ test_files = [
115
+ "Show - 1x01.srt",
116
+ "Show - 1x08.srt",
117
+ "Show - 2x01.srt", # Should not match season 1
118
+ ]
119
+
120
+ self._create_test_files(test_files)
121
+
122
+ reference_files = self.identifier.get_reference_files(1)
123
+
124
+ # Should find 2 files for season 1
125
+ self.assertEqual(len(reference_files), 2)
126
+
127
+ matched_names = [f.name for f in reference_files]
128
+ self.assertIn("Show - 1x01.srt", matched_names)
129
+ self.assertIn("Show - 1x08.srt", matched_names)
130
+ self.assertNotIn("Show - 2x01.srt", matched_names)
131
+
132
+ def test_pattern_matching_mixed_formats(self):
133
+ """Test pattern matching with mixed file formats."""
134
+ test_files = [
135
+ "Show - S01E01.srt",
136
+ "Show - S1E02.srt",
137
+ "Show - 01x03.srt",
138
+ "Show - 1x04.srt",
139
+ "Show - S02E01.srt", # Different season
140
+ "Show - episode1.srt", # No matching pattern
141
+ ]
142
+
143
+ self._create_test_files(test_files)
144
+
145
+ reference_files = self.identifier.get_reference_files(1)
146
+
147
+ # Should find 4 files for season 1 (all matching patterns)
148
+ self.assertEqual(len(reference_files), 4)
149
+
150
+ matched_names = [f.name for f in reference_files]
151
+ self.assertIn("Show - S01E01.srt", matched_names)
152
+ self.assertIn("Show - S1E02.srt", matched_names)
153
+ self.assertIn("Show - 01x03.srt", matched_names)
154
+ self.assertIn("Show - 1x04.srt", matched_names)
155
+ self.assertNotIn("Show - S02E01.srt", matched_names)
156
+ self.assertNotIn("Show - episode1.srt", matched_names)
157
+
158
+ def test_case_insensitive_matching(self):
159
+ """Test that pattern matching is case insensitive."""
160
+ test_files = [
161
+ "show - s01e01.srt", # lowercase s and e
162
+ "SHOW - S01E02.SRT", # uppercase everything
163
+ "Show - S01e03.srt", # mixed case
164
+ ]
165
+
166
+ self._create_test_files(test_files)
167
+
168
+ reference_files = self.identifier.get_reference_files(1)
169
+
170
+ # Should find all 3 files regardless of case
171
+ self.assertEqual(len(reference_files), 3)
172
+
173
+ def test_dexter_new_blood_issue_reproduction(self):
174
+ """Test the specific issue from bug report with Dexter New Blood files."""
175
+ # Create files matching the exact pattern from the bug report
176
+ test_files = [
177
+ "Dexter - New Blood (2021) - S01E01.srt",
178
+ "Dexter - New Blood (2021) - S01E02.srt",
179
+ "Dexter - New Blood (2021) - S01E03.srt",
180
+ "Dexter - New Blood (2021) - S01E04.srt",
181
+ "Dexter - New Blood (2021) - S01E05.srt",
182
+ "Dexter - New Blood (2021) - S01E06.srt",
183
+ "Dexter - New Blood (2021) - S01E07.srt",
184
+ "Dexter - New Blood (2021) - S01E08.srt",
185
+ "Dexter - New Blood (2021) - S01E09.srt",
186
+ "Dexter - New Blood (2021) - S01E10.srt",
187
+ ]
188
+
189
+ # Update the identifier to use the exact show name from the bug report
190
+ show_name = "Dexter - New Blood (2021)"
191
+ self.identifier.show_name = show_name
192
+
193
+ self._create_test_files(test_files, show_name)
194
+
195
+ reference_files = self.identifier.get_reference_files(1)
196
+
197
+ # Should find all 10 files - this was the bug that was failing
198
+ self.assertEqual(len(reference_files), 10,
199
+ f"Expected 10 files but found {len(reference_files)}. "
200
+ f"Files found: {[f.name for f in reference_files]}")
201
+
202
+ def test_no_duplicate_files(self):
203
+ """Test that duplicate matches are removed properly."""
204
+ # Create a file that could match multiple patterns
205
+ test_files = [
206
+ "Show - S01E01.srt", # Could match both S01E and 01x patterns if logic is wrong
207
+ ]
208
+
209
+ self._create_test_files(test_files)
210
+
211
+ reference_files = self.identifier.get_reference_files(1)
212
+
213
+ # Should find only 1 unique file, not duplicates
214
+ self.assertEqual(len(reference_files), 1)
215
+
216
+ # Verify it's the correct file
217
+ self.assertEqual(reference_files[0].name, "Show - S01E01.srt")
218
+
219
+ def test_empty_directory(self):
220
+ """Test behavior when no matching files exist."""
221
+ # Create the directory but no files
222
+ show_dir = self.cache_dir / "data" / "Test Show (2023)"
223
+ show_dir.mkdir(parents=True, exist_ok=True)
224
+
225
+ reference_files = self.identifier.get_reference_files(1)
226
+
227
+ # Should find no files
228
+ self.assertEqual(len(reference_files), 0)
229
+
230
+ def test_directory_does_not_exist(self):
231
+ """Test behavior when the show directory doesn't exist."""
232
+ # Don't create the directory
233
+ reference_files = self.identifier.get_reference_files(1)
234
+
235
+ # Should find no files (glob returns empty on non-existent directory)
236
+ self.assertEqual(len(reference_files), 0)
237
+
238
+ def test_caching_functionality(self):
239
+ """Test that results are properly cached."""
240
+ test_files = ["Show - S01E01.srt"]
241
+ self._create_test_files(test_files)
242
+
243
+ # First call
244
+ reference_files_1 = self.identifier.get_reference_files(1)
245
+
246
+ # Second call should return cached result
247
+ reference_files_2 = self.identifier.get_reference_files(1)
248
+
249
+ # Should be the same result
250
+ self.assertEqual(reference_files_1, reference_files_2)
251
+
252
+ # Verify it's actually using the cache by checking the cache directly
253
+ cache_key = ("Test Show (2023)", 1)
254
+ self.assertIn(cache_key, self.identifier.reference_files_cache)
255
+
256
+ def test_different_seasons(self):
257
+ """Test that different seasons are handled correctly."""
258
+ test_files = [
259
+ "Show - S01E01.srt",
260
+ "Show - S01E02.srt",
261
+ "Show - S02E01.srt",
262
+ "Show - S02E02.srt",
263
+ "Show - S03E01.srt",
264
+ ]
265
+
266
+ self._create_test_files(test_files)
267
+
268
+ # Test season 1
269
+ season1_files = self.identifier.get_reference_files(1)
270
+ self.assertEqual(len(season1_files), 2)
271
+
272
+ # Test season 2
273
+ season2_files = self.identifier.get_reference_files(2)
274
+ self.assertEqual(len(season2_files), 2)
275
+
276
+ # Test season 3
277
+ season3_files = self.identifier.get_reference_files(3)
278
+ self.assertEqual(len(season3_files), 1)
279
+
280
+ # Test non-existent season
281
+ season4_files = self.identifier.get_reference_files(4)
282
+ self.assertEqual(len(season4_files), 0)
283
+
284
+
285
+ if __name__ == '__main__':
286
+ unittest.main()