mkv-episode-matcher 0.5.0__tar.gz → 0.7.0__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.
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/.coverage +0 -0
- mkv_episode_matcher-0.7.0/CHANGELOG.md +51 -0
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/PKG-INFO +10 -13
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/README.md +8 -3
- mkv_episode_matcher-0.7.0/docs/changelog.md +36 -0
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/docs/cli.md +7 -8
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/docs/configuration.md +1 -16
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/docs/installation.md +2 -8
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/docs/tips.md +14 -3
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher/__init__.py +2 -2
- mkv_episode_matcher-0.7.0/mkv_episode_matcher/__main__.py +326 -0
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher/config.py +0 -3
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher/episode_identification.py +164 -124
- mkv_episode_matcher-0.7.0/mkv_episode_matcher/episode_matcher.py +152 -0
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher/subtitle_utils.py +26 -25
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher/utils.py +74 -57
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher.egg-info/PKG-INFO +10 -13
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher.egg-info/SOURCES.txt +2 -11
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher.egg-info/requires.txt +1 -11
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/pyproject.toml +1 -45
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/setup.cfg +1 -1
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/tests/test_main.py +19 -29
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/uv.lock +555 -534
- mkv_episode_matcher-0.5.0/mkv_episode_matcher/__main__.py +0 -180
- mkv_episode_matcher-0.5.0/mkv_episode_matcher/episode_matcher.py +0 -105
- mkv_episode_matcher-0.5.0/mkv_episode_matcher/libraries/pgs2srt/.gitignore +0 -2
- mkv_episode_matcher-0.5.0/mkv_episode_matcher/libraries/pgs2srt/Libraries/SubZero/SubZero.py +0 -321
- mkv_episode_matcher-0.5.0/mkv_episode_matcher/libraries/pgs2srt/Libraries/SubZero/dictionaries/data.py +0 -16700
- mkv_episode_matcher-0.5.0/mkv_episode_matcher/libraries/pgs2srt/Libraries/SubZero/post_processing.py +0 -260
- mkv_episode_matcher-0.5.0/mkv_episode_matcher/libraries/pgs2srt/README.md +0 -26
- mkv_episode_matcher-0.5.0/mkv_episode_matcher/libraries/pgs2srt/imagemaker.py +0 -89
- mkv_episode_matcher-0.5.0/mkv_episode_matcher/libraries/pgs2srt/pgs2srt.py +0 -150
- mkv_episode_matcher-0.5.0/mkv_episode_matcher/libraries/pgs2srt/pgsreader.py +0 -225
- mkv_episode_matcher-0.5.0/mkv_episode_matcher/libraries/pgs2srt/requirements.txt +0 -4
- mkv_episode_matcher-0.5.0/mkv_episode_matcher/mkv_to_srt.py +0 -302
- mkv_episode_matcher-0.5.0/tests/__init__.py +0 -0
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/.gitattributes +0 -0
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/.github/funding.yml +0 -0
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/.github/workflows/documentation.yml +0 -0
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/.github/workflows/python-publish.yml +0 -0
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/.github/workflows/tests.yml +0 -0
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/.gitignore +0 -0
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/.gitmodules +0 -0
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/.python-version +0 -0
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/.vscode/settings.json +0 -0
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/docs/api/index.md +0 -0
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/docs/quickstart.md +0 -0
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkdocs.yml +0 -0
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher/.gitattributes +0 -0
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher/tmdb_client.py +0 -0
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher.egg-info/dependency_links.txt +0 -0
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher.egg-info/entry_points.txt +0 -0
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher.egg-info/top_level.txt +0 -0
- {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/setup.py +0 -0
- {mkv_episode_matcher-0.5.0/mkv_episode_matcher/libraries/pgs2srt → mkv_episode_matcher-0.7.0/tests}/__init__.py +0 -0
|
Binary file
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.7.0] - 2025-03-05
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Rich UI with color-coded output and progress indicators
|
|
12
|
+
- Interactive season selection interface
|
|
13
|
+
- Visual confirmation panels for operations
|
|
14
|
+
- GPU support check command
|
|
15
|
+
- Masked API key display for improved security
|
|
16
|
+
- Verbose output option for detailed logging
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- Enhanced CLI interface with better visual feedback
|
|
20
|
+
- Improved error messages with color coding
|
|
21
|
+
- Updated documentation to reflect new UI features
|
|
22
|
+
|
|
23
|
+
## [0.6.0] - 2025-03-02
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
- Comprehensive documentation including installation, configuration, and CLI guides
|
|
27
|
+
- Quick start guide with common usage examples
|
|
28
|
+
- Tips and tricks documentation with best practices
|
|
29
|
+
- Detailed changelog structure
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
- Improved project metadata and description
|
|
33
|
+
- Updated version number in setup.cfg
|
|
34
|
+
|
|
35
|
+
### Removed
|
|
36
|
+
- Removed OCR support and Tesseract dependency
|
|
37
|
+
- Removed unused code
|
|
38
|
+
|
|
39
|
+
## [0.5.0] - 2025-02-23
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
- Try to use tiny version of OpenAI Whisper for initial matching
|
|
43
|
+
- Fall back to base model if tiny model fails
|
|
44
|
+
- Progressive matching in 30s intervals (was 300s)
|
|
45
|
+
|
|
46
|
+
### Removed
|
|
47
|
+
- Removed unused code
|
|
48
|
+
|
|
49
|
+
[0.7.0]: https://github.com/Jsakkos/mkv-episode-matcher/releases/tag/v0.7.0
|
|
50
|
+
[0.6.0]: https://github.com/Jsakkos/mkv-episode-matcher/releases/tag/v0.6.0
|
|
51
|
+
[0.5.0]: https://github.com/Jsakkos/mkv-episode-matcher/releases/tag/v0.5.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: mkv-episode-matcher
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
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
|
|
@@ -21,22 +21,14 @@ Requires-Dist: ffmpeg>=1.4
|
|
|
21
21
|
Requires-Dist: loguru>=0.7.2
|
|
22
22
|
Requires-Dist: openai-whisper>=20240930
|
|
23
23
|
Requires-Dist: opensubtitlescom>=0.1.5
|
|
24
|
-
Requires-Dist: pytesseract>=0.3.13
|
|
25
24
|
Requires-Dist: rapidfuzz>=3.10.1
|
|
26
25
|
Requires-Dist: requests>=2.32.3
|
|
26
|
+
Requires-Dist: rich[jupyter]>=13.9.4
|
|
27
27
|
Requires-Dist: tmdb-client>=0.0.1
|
|
28
28
|
Requires-Dist: torch>=2.5.1
|
|
29
29
|
Requires-Dist: torchaudio>=2.5.1
|
|
30
30
|
Requires-Dist: torchvision>=0.20.1
|
|
31
31
|
Requires-Dist: wave>=0.0.2
|
|
32
|
-
Provides-Extra: cpu
|
|
33
|
-
Requires-Dist: torch>=2.5.1; extra == "cpu"
|
|
34
|
-
Requires-Dist: torchvision>=0.20.1; extra == "cpu"
|
|
35
|
-
Requires-Dist: torchaudio>=2.5.1; extra == "cpu"
|
|
36
|
-
Provides-Extra: cu124
|
|
37
|
-
Requires-Dist: torch>=2.5.1; extra == "cu124"
|
|
38
|
-
Requires-Dist: torchvision>=0.20.1; extra == "cu124"
|
|
39
|
-
Requires-Dist: torchaudio>=2.5.1; extra == "cu124"
|
|
40
32
|
|
|
41
33
|
# MKV Episode Matcher
|
|
42
34
|
|
|
@@ -48,25 +40,26 @@ Requires-Dist: torchaudio>=2.5.1; extra == "cu124"
|
|
|
48
40
|
[](https://github.com/Jsakkos/mkv-episode-matcher/commits/main)
|
|
49
41
|
[](https://github.com/Jsakkos/mkv-episode-matcher/issues)
|
|
50
42
|
[](https://github.com/Jsakkos/mkv-episode-matcher/actions/workflows/tests.yml)
|
|
51
|
-
[](https://codecov.io/gh/Jsakkos/mkv-episode-matcher)
|
|
43
|
+
[](https://codecov.io/gh/Jsakkos/mkv-episode-matcher/)
|
|
52
44
|
|
|
53
45
|
Automatically match and rename your MKV TV episodes using The Movie Database (TMDb).
|
|
54
46
|
|
|
55
47
|
## Features
|
|
56
48
|
|
|
57
49
|
- 🎯 **Automatic Episode Matching**: Uses TMDb to accurately identify episodes
|
|
50
|
+
- 🎨 **Rich User Interface**: Color-coded output and progress indicators
|
|
58
51
|
- 📝 **Subtitle Extraction**: Extracts subtitles from MKV files
|
|
59
|
-
-
|
|
52
|
+
- 🔊 **Speech Recognition**: Uses Whisper for accurate episode identification
|
|
60
53
|
- 🚀 **Multi-threaded**: Fast processing of multiple files
|
|
61
54
|
- ⬇️ **Subtitle Downloads**: Integration with OpenSubtitles
|
|
62
55
|
- ✨ **Bulk Processing**: Handle entire seasons at once
|
|
63
56
|
- 🧪 **Dry Run Mode**: Test changes before applying
|
|
57
|
+
- 🎮 **Interactive Mode**: User-friendly season selection and configuration
|
|
64
58
|
|
|
65
59
|
## Prerequisites
|
|
66
60
|
|
|
67
61
|
- Python 3.9 or higher
|
|
68
62
|
- [FFmpeg](https://ffmpeg.org/download.html) installed and available in system PATH
|
|
69
|
-
- [Tesseract OCR](https://github.com/UB-Mannheim/tesseract/wiki) installed (required for image-based subtitle processing)
|
|
70
63
|
- TMDb API key (optional, for subtitle downloads)
|
|
71
64
|
- OpenSubtitles account (optional, for subtitle downloads)
|
|
72
65
|
|
|
@@ -135,3 +128,7 @@ Distributed under the MIT License. See `LICENSE` for more information.
|
|
|
135
128
|
## Documentation
|
|
136
129
|
|
|
137
130
|
Full documentation is available at [https://jsakkos.github.io/mkv-episode-matcher/](https://jsakkos.github.io/mkv-episode-matcher/)
|
|
131
|
+
|
|
132
|
+
## Changelog
|
|
133
|
+
|
|
134
|
+
See [CHANGELOG.md](CHANGELOG.md) for a detailed list of changes.
|
|
@@ -8,25 +8,26 @@
|
|
|
8
8
|
[](https://github.com/Jsakkos/mkv-episode-matcher/commits/main)
|
|
9
9
|
[](https://github.com/Jsakkos/mkv-episode-matcher/issues)
|
|
10
10
|
[](https://github.com/Jsakkos/mkv-episode-matcher/actions/workflows/tests.yml)
|
|
11
|
-
[](https://codecov.io/gh/Jsakkos/mkv-episode-matcher)
|
|
11
|
+
[](https://codecov.io/gh/Jsakkos/mkv-episode-matcher/)
|
|
12
12
|
|
|
13
13
|
Automatically match and rename your MKV TV episodes using The Movie Database (TMDb).
|
|
14
14
|
|
|
15
15
|
## Features
|
|
16
16
|
|
|
17
17
|
- 🎯 **Automatic Episode Matching**: Uses TMDb to accurately identify episodes
|
|
18
|
+
- 🎨 **Rich User Interface**: Color-coded output and progress indicators
|
|
18
19
|
- 📝 **Subtitle Extraction**: Extracts subtitles from MKV files
|
|
19
|
-
-
|
|
20
|
+
- 🔊 **Speech Recognition**: Uses Whisper for accurate episode identification
|
|
20
21
|
- 🚀 **Multi-threaded**: Fast processing of multiple files
|
|
21
22
|
- ⬇️ **Subtitle Downloads**: Integration with OpenSubtitles
|
|
22
23
|
- ✨ **Bulk Processing**: Handle entire seasons at once
|
|
23
24
|
- 🧪 **Dry Run Mode**: Test changes before applying
|
|
25
|
+
- 🎮 **Interactive Mode**: User-friendly season selection and configuration
|
|
24
26
|
|
|
25
27
|
## Prerequisites
|
|
26
28
|
|
|
27
29
|
- Python 3.9 or higher
|
|
28
30
|
- [FFmpeg](https://ffmpeg.org/download.html) installed and available in system PATH
|
|
29
|
-
- [Tesseract OCR](https://github.com/UB-Mannheim/tesseract/wiki) installed (required for image-based subtitle processing)
|
|
30
31
|
- TMDb API key (optional, for subtitle downloads)
|
|
31
32
|
- OpenSubtitles account (optional, for subtitle downloads)
|
|
32
33
|
|
|
@@ -95,3 +96,7 @@ Distributed under the MIT License. See `LICENSE` for more information.
|
|
|
95
96
|
## Documentation
|
|
96
97
|
|
|
97
98
|
Full documentation is available at [https://jsakkos.github.io/mkv-episode-matcher/](https://jsakkos.github.io/mkv-episode-matcher/)
|
|
99
|
+
|
|
100
|
+
## Changelog
|
|
101
|
+
|
|
102
|
+
See [CHANGELOG.md](CHANGELOG.md) for a detailed list of changes.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
For a complete list of changes, see [CHANGELOG.md](../CHANGELOG.md) in the repository root.
|
|
4
|
+
|
|
5
|
+
## Latest Changes
|
|
6
|
+
|
|
7
|
+
## [0.6.0] - 2025-02-24
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- Comprehensive documentation including installation, configuration, and CLI guides
|
|
11
|
+
- Quick start guide with common usage examples
|
|
12
|
+
- Tips and tricks documentation with best practices
|
|
13
|
+
- Detailed changelog structure
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- Improved project metadata and description
|
|
17
|
+
- Updated version number in setup.cfg
|
|
18
|
+
|
|
19
|
+
### Removed
|
|
20
|
+
- Removed OCR support and Tesseract dependency
|
|
21
|
+
- Removed unused code
|
|
22
|
+
|
|
23
|
+
## [0.5.0] - 2025-02-23
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
- Try to use tiny version of OpenAI Whisper for initial matching
|
|
27
|
+
- Fall back to base model if tiny model fails
|
|
28
|
+
- Progressive matching in 30s intervals (was 300s)
|
|
29
|
+
|
|
30
|
+
### Removed
|
|
31
|
+
- Removed unused code
|
|
32
|
+
|
|
33
|
+
[0.6.0]: https://github.com/Jsakkos/mkv-episode-matcher/releases/tag/v0.6.0
|
|
34
|
+
[0.5.0]: https://github.com/Jsakkos/mkv-episode-matcher/releases/tag/v0.5.0
|
|
35
|
+
|
|
36
|
+
For older versions and complete changelog history, please visit our [GitHub releases page](https://github.com/Jsakkos/mkv-episode-matcher/releases).
|
|
@@ -16,14 +16,13 @@ mkv-match --show-dir "/path/to/show" --season 1
|
|
|
16
16
|
|
|
17
17
|
## Command Options
|
|
18
18
|
|
|
19
|
-
| Option
|
|
20
|
-
|
|
|
21
|
-
| `--show-dir`
|
|
22
|
-
| `--season`
|
|
23
|
-
| `--dry-run`
|
|
24
|
-
| `--get-subs`
|
|
25
|
-
| `--tmdb-api-key`
|
|
26
|
-
| `--tesseract-path` | Path to Tesseract | None |
|
|
19
|
+
| Option | Description | Default |
|
|
20
|
+
| ---------------- | --------------------------- | ---------- |
|
|
21
|
+
| `--show-dir` | Show directory path | None |
|
|
22
|
+
| `--season` | Season number to process | None (all) |
|
|
23
|
+
| `--dry-run` | Test without making changes | False |
|
|
24
|
+
| `--get-subs` | Download subtitles | False |
|
|
25
|
+
| `--tmdb-api-key` | TMDb API key | None |
|
|
27
26
|
|
|
28
27
|
## Examples
|
|
29
28
|
|
|
@@ -21,7 +21,6 @@ open_subtitles_api_key = your_opensubs_key
|
|
|
21
21
|
open_subtitles_user_agent = your_user_agent
|
|
22
22
|
open_subtitles_username = your_username
|
|
23
23
|
open_subtitles_password = your_password
|
|
24
|
-
tesseract_path = /path/to/tesseract
|
|
25
24
|
```
|
|
26
25
|
|
|
27
26
|
## Command Line Configuration
|
|
@@ -34,8 +33,7 @@ mkv-match \
|
|
|
34
33
|
--show-dir "/path/to/shows" \
|
|
35
34
|
--season 1 \
|
|
36
35
|
--dry-run true \
|
|
37
|
-
--get-subs true
|
|
38
|
-
--tesseract-path "/path/to/tesseract"
|
|
36
|
+
--get-subs true
|
|
39
37
|
```
|
|
40
38
|
|
|
41
39
|
## Environment Variables
|
|
@@ -94,16 +92,3 @@ Adjust based on your system's capabilities:
|
|
|
94
92
|
- Default: 4 threads
|
|
95
93
|
- Minimum: 1 thread
|
|
96
94
|
- Maximum: Number of CPU cores
|
|
97
|
-
|
|
98
|
-
### OCR Configuration
|
|
99
|
-
|
|
100
|
-
```ini
|
|
101
|
-
[Config]
|
|
102
|
-
tesseract_path = /path/to/tesseract
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
Required for processing image-based subtitles. Common paths:
|
|
106
|
-
- Windows: `C:\Program Files\Tesseract-OCR\tesseract.exe`
|
|
107
|
-
- Linux/macOS: `/usr/bin/tesseract`
|
|
108
|
-
|
|
109
|
-
Make sure Tesseract OCR is properly installed before using this feature.
|
|
@@ -10,13 +10,7 @@
|
|
|
10
10
|
- macOS: `brew install ffmpeg`
|
|
11
11
|
- Verify installation: `ffmpeg -version`
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
- Windows: Install from [UB-Mannheim](https://github.com/UB-Mannheim/tesseract/wiki)
|
|
15
|
-
- Linux: `sudo apt install tesseract-ocr` or equivalent
|
|
16
|
-
- macOS: `brew install tesseract`
|
|
17
|
-
- Verify installation: `tesseract --version`
|
|
18
|
-
|
|
19
|
-
Make sure both FFmpeg and Tesseract are added to your system PATH.
|
|
13
|
+
Make sure FFmpeg is added to your system PATH.
|
|
20
14
|
|
|
21
15
|
## Basic Installation
|
|
22
16
|
|
|
@@ -43,7 +37,6 @@ pip install -U torch torchvision torchaudio --index-url https://download.pytorch
|
|
|
43
37
|
mkv-match --check-gpu true
|
|
44
38
|
```
|
|
45
39
|
|
|
46
|
-
|
|
47
40
|
### Development Installation
|
|
48
41
|
|
|
49
42
|
For contributing or development:
|
|
@@ -80,6 +73,7 @@ uv sync --dev
|
|
|
80
73
|
|
|
81
74
|
### For CPU-Only
|
|
82
75
|
- No special requirements beyond Python 3.9+
|
|
76
|
+
- Note: Speech recognition will be slower on CPU
|
|
83
77
|
|
|
84
78
|
## Verification
|
|
85
79
|
|
|
@@ -21,7 +21,7 @@ TV Shows/
|
|
|
21
21
|
1. **Thread Configuration**
|
|
22
22
|
```ini
|
|
23
23
|
[Config]
|
|
24
|
-
max_threads = 4 # Adjust based on CPU
|
|
24
|
+
max_threads = 4 # Adjust based on CPU/GPU capability
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
2. **Batch Processing**
|
|
@@ -32,6 +32,12 @@ TV Shows/
|
|
|
32
32
|
done
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
+
3. **Speech Recognition**
|
|
36
|
+
- Uses OpenAI Whisper for audio analysis
|
|
37
|
+
- Tiny model is tried first for speed
|
|
38
|
+
- Falls back to base model if needed
|
|
39
|
+
- Works with both DVD and Blu-ray sources
|
|
40
|
+
|
|
35
41
|
### Error Handling
|
|
36
42
|
|
|
37
43
|
1. Always use dry-run first:
|
|
@@ -87,6 +93,11 @@ process_show(
|
|
|
87
93
|
- Use rate limiting in configuration
|
|
88
94
|
- Implement exponential backoff
|
|
89
95
|
|
|
96
|
+
3. **Speech Recognition**
|
|
97
|
+
- GPU recommended for faster processing
|
|
98
|
+
- Processing happens in 30s intervals
|
|
99
|
+
- More accurate than OCR-based methods
|
|
100
|
+
|
|
90
101
|
3. **Memory Usage**
|
|
91
102
|
- Reduce max_threads
|
|
92
103
|
- Process seasons separately
|
|
@@ -97,8 +108,8 @@ process_show(
|
|
|
97
108
|
|
|
98
109
|
1. Remove temporary files:
|
|
99
110
|
```python
|
|
100
|
-
from mkv_episode_matcher.utils import
|
|
101
|
-
|
|
111
|
+
from mkv_episode_matcher.utils import cleanup_temp_files
|
|
112
|
+
cleanup_temp_files(show_dir)
|
|
102
113
|
```
|
|
103
114
|
|
|
104
115
|
2. Clear cache:
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"""MKV Episode Matcher package."""
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
3
4
|
|
|
4
5
|
try:
|
|
5
6
|
__version__ = version("mkv-episode-matcher")
|
|
6
7
|
except PackageNotFoundError:
|
|
7
8
|
# package is not installed
|
|
8
9
|
__version__ = "unknown"
|
|
9
|
-
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
# __main__.py (enhanced version)
|
|
2
|
+
import argparse
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from loguru import logger
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.panel import Panel
|
|
10
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
11
|
+
from rich.prompt import Confirm, Prompt
|
|
12
|
+
|
|
13
|
+
from mkv_episode_matcher import __version__
|
|
14
|
+
from mkv_episode_matcher.config import get_config, set_config
|
|
15
|
+
|
|
16
|
+
# Initialize rich console for better output
|
|
17
|
+
console = Console()
|
|
18
|
+
|
|
19
|
+
# Log the start of the application
|
|
20
|
+
logger.info("Starting the application")
|
|
21
|
+
|
|
22
|
+
# Check if the configuration directory exists, if not create it
|
|
23
|
+
CONFIG_DIR = os.path.join(os.path.expanduser("~"), ".mkv-episode-matcher")
|
|
24
|
+
if not os.path.exists(CONFIG_DIR):
|
|
25
|
+
os.makedirs(CONFIG_DIR)
|
|
26
|
+
|
|
27
|
+
# Define the paths for the configuration file and cache directory
|
|
28
|
+
CONFIG_FILE = os.path.join(CONFIG_DIR, "config.ini")
|
|
29
|
+
CACHE_DIR = os.path.join(CONFIG_DIR, "cache")
|
|
30
|
+
|
|
31
|
+
# Check if the cache directory exists, if not create it
|
|
32
|
+
if not os.path.exists(CACHE_DIR):
|
|
33
|
+
os.makedirs(CACHE_DIR)
|
|
34
|
+
|
|
35
|
+
# Check if logs directory exists, if not create it
|
|
36
|
+
log_dir = os.path.join(CONFIG_DIR, "logs")
|
|
37
|
+
if not os.path.exists(log_dir):
|
|
38
|
+
os.mkdir(log_dir)
|
|
39
|
+
logger.remove()
|
|
40
|
+
# Add a new handler for stdout logs
|
|
41
|
+
logger.add(
|
|
42
|
+
os.path.join(log_dir, "stdout.log"),
|
|
43
|
+
format="{time} {level} {message}",
|
|
44
|
+
level="INFO",
|
|
45
|
+
rotation="10 MB",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Add a new handler for error logs
|
|
49
|
+
logger.add(os.path.join(log_dir, "stderr.log"), level="ERROR", rotation="10 MB")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def print_welcome_message():
|
|
53
|
+
"""Print a stylized welcome message."""
|
|
54
|
+
console.print(
|
|
55
|
+
Panel.fit(
|
|
56
|
+
f"[bold blue]MKV Episode Matcher v{__version__}[/bold blue]\n"
|
|
57
|
+
"[cyan]Automatically match and rename your MKV TV episodes[/cyan]",
|
|
58
|
+
border_style="blue",
|
|
59
|
+
padding=(1, 4),
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
console.print()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def confirm_api_key(config_value: Optional[str], key_name: str, description: str) -> str:
|
|
66
|
+
"""
|
|
67
|
+
Confirm if the user wants to use an existing API key or enter a new one.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
config_value: The current value from the config
|
|
71
|
+
key_name: The name of the key
|
|
72
|
+
description: Description of the key for user information
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
The API key to use
|
|
76
|
+
"""
|
|
77
|
+
if config_value:
|
|
78
|
+
console.print(f"[cyan]{key_name}:[/cyan] {description}")
|
|
79
|
+
console.print(f"Current value: [green]{mask_api_key(config_value)}[/green]")
|
|
80
|
+
if Confirm.ask("Use existing key?", default=True):
|
|
81
|
+
return config_value
|
|
82
|
+
|
|
83
|
+
return Prompt.ask(f"Enter your {key_name}")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def mask_api_key(key: str) -> str:
|
|
87
|
+
"""Mask the API key for display purposes."""
|
|
88
|
+
if not key:
|
|
89
|
+
return ""
|
|
90
|
+
if len(key) <= 8:
|
|
91
|
+
return "*" * len(key)
|
|
92
|
+
return key[:4] + "*" * (len(key) - 8) + key[-4:]
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def select_season(seasons):
|
|
96
|
+
"""
|
|
97
|
+
Allow user to select a season from a list.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
seasons: List of available seasons
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Selected season number or None for all seasons
|
|
104
|
+
"""
|
|
105
|
+
console.print("[bold cyan]Available Seasons:[/bold cyan]")
|
|
106
|
+
for i, season in enumerate(seasons, 1):
|
|
107
|
+
season_num = os.path.basename(season).replace("Season ", "")
|
|
108
|
+
console.print(f" {i}. Season {season_num}")
|
|
109
|
+
|
|
110
|
+
console.print(f" 0. All Seasons")
|
|
111
|
+
|
|
112
|
+
choice = Prompt.ask(
|
|
113
|
+
"Select a season number (0 for all)",
|
|
114
|
+
choices=[str(i) for i in range(len(seasons) + 1)],
|
|
115
|
+
default="0"
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
if int(choice) == 0:
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
selected_season = seasons[int(choice) - 1]
|
|
122
|
+
return int(os.path.basename(selected_season).replace("Season ", ""))
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@logger.catch
|
|
126
|
+
def main():
|
|
127
|
+
"""
|
|
128
|
+
Entry point of the application with enhanced user interface.
|
|
129
|
+
"""
|
|
130
|
+
print_welcome_message()
|
|
131
|
+
|
|
132
|
+
# Parse command-line arguments
|
|
133
|
+
parser = argparse.ArgumentParser(
|
|
134
|
+
description="Automatically match and rename your MKV TV episodes",
|
|
135
|
+
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
136
|
+
)
|
|
137
|
+
parser.add_argument(
|
|
138
|
+
"--version",
|
|
139
|
+
action="version",
|
|
140
|
+
version=f"%(prog)s {__version__}",
|
|
141
|
+
help="Show the version number and exit",
|
|
142
|
+
)
|
|
143
|
+
parser.add_argument("--tmdb-api-key", help="TMDb API key")
|
|
144
|
+
parser.add_argument("--show-dir", help="Main directory of the show")
|
|
145
|
+
parser.add_argument(
|
|
146
|
+
"--season",
|
|
147
|
+
type=int,
|
|
148
|
+
default=None,
|
|
149
|
+
nargs="?",
|
|
150
|
+
help="Specify the season number to be processed (default: all seasons)",
|
|
151
|
+
)
|
|
152
|
+
parser.add_argument(
|
|
153
|
+
"--dry-run",
|
|
154
|
+
action="store_true",
|
|
155
|
+
help="Don't rename any files, just show what would happen",
|
|
156
|
+
)
|
|
157
|
+
parser.add_argument(
|
|
158
|
+
"--get-subs",
|
|
159
|
+
action="store_true",
|
|
160
|
+
help="Download subtitles for the show",
|
|
161
|
+
)
|
|
162
|
+
parser.add_argument(
|
|
163
|
+
"--check-gpu",
|
|
164
|
+
action="store_true",
|
|
165
|
+
help="Check if GPU is available for faster processing",
|
|
166
|
+
)
|
|
167
|
+
parser.add_argument(
|
|
168
|
+
"--verbose", "-v",
|
|
169
|
+
action="store_true",
|
|
170
|
+
help="Enable verbose output",
|
|
171
|
+
)
|
|
172
|
+
parser.add_argument(
|
|
173
|
+
"--confidence",
|
|
174
|
+
type=float,
|
|
175
|
+
default=0.7,
|
|
176
|
+
help="Set confidence threshold for episode matching (0.0-1.0)",
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
args = parser.parse_args()
|
|
180
|
+
if args.verbose:
|
|
181
|
+
console.print("[bold cyan]Command-line Arguments[/bold cyan]")
|
|
182
|
+
console.print(args)
|
|
183
|
+
if args.check_gpu:
|
|
184
|
+
from mkv_episode_matcher.utils import check_gpu_support
|
|
185
|
+
with console.status("[bold green]Checking GPU support..."):
|
|
186
|
+
check_gpu_support()
|
|
187
|
+
return
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
logger.debug(f"Command-line arguments: {args}")
|
|
191
|
+
|
|
192
|
+
# Load configuration once
|
|
193
|
+
config = get_config(CONFIG_FILE)
|
|
194
|
+
|
|
195
|
+
# Get TMDb API key
|
|
196
|
+
tmdb_api_key = args.tmdb_api_key or config.get("tmdb_api_key")
|
|
197
|
+
|
|
198
|
+
open_subtitles_api_key = config.get("open_subtitles_api_key")
|
|
199
|
+
open_subtitles_user_agent = config.get("open_subtitles_user_agent")
|
|
200
|
+
open_subtitles_username = config.get("open_subtitles_username")
|
|
201
|
+
open_subtitles_password = config.get("open_subtitles_password")
|
|
202
|
+
|
|
203
|
+
if args.get_subs:
|
|
204
|
+
console.print("[bold cyan]Subtitle Download Configuration[/bold cyan]")
|
|
205
|
+
|
|
206
|
+
tmdb_api_key = confirm_api_key(
|
|
207
|
+
tmdb_api_key,
|
|
208
|
+
"TMDb API key",
|
|
209
|
+
"Used to lookup show and episode information"
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
open_subtitles_api_key = confirm_api_key(
|
|
213
|
+
open_subtitles_api_key,
|
|
214
|
+
"OpenSubtitles API key",
|
|
215
|
+
"Required for subtitle downloads"
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
open_subtitles_user_agent = confirm_api_key(
|
|
219
|
+
open_subtitles_user_agent,
|
|
220
|
+
"OpenSubtitles User Agent",
|
|
221
|
+
"Required for subtitle downloads"
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
open_subtitles_username = confirm_api_key(
|
|
225
|
+
open_subtitles_username,
|
|
226
|
+
"OpenSubtitles Username",
|
|
227
|
+
"Account username for OpenSubtitles"
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
open_subtitles_password = confirm_api_key(
|
|
231
|
+
open_subtitles_password,
|
|
232
|
+
"OpenSubtitles Password",
|
|
233
|
+
"Account password for OpenSubtitles"
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
# Use config for show directory
|
|
237
|
+
show_dir = args.show_dir or config.get("show_dir")
|
|
238
|
+
if not show_dir:
|
|
239
|
+
show_dir = Prompt.ask("Enter the main directory of the show")
|
|
240
|
+
|
|
241
|
+
logger.info(f"Show Directory: {show_dir}")
|
|
242
|
+
if not os.path.exists(show_dir):
|
|
243
|
+
console.print(f"[bold red]Error:[/bold red] Show directory '{show_dir}' does not exist.")
|
|
244
|
+
return
|
|
245
|
+
|
|
246
|
+
if not show_dir:
|
|
247
|
+
show_dir = os.getcwd()
|
|
248
|
+
console.print(f"Using current directory: [cyan]{show_dir}[/cyan]")
|
|
249
|
+
|
|
250
|
+
logger.debug(f"Show Directory: {show_dir}")
|
|
251
|
+
|
|
252
|
+
# Set the configuration
|
|
253
|
+
set_config(
|
|
254
|
+
tmdb_api_key,
|
|
255
|
+
open_subtitles_api_key,
|
|
256
|
+
open_subtitles_user_agent,
|
|
257
|
+
open_subtitles_username,
|
|
258
|
+
open_subtitles_password,
|
|
259
|
+
show_dir,
|
|
260
|
+
CONFIG_FILE,
|
|
261
|
+
)
|
|
262
|
+
logger.info("Configuration set")
|
|
263
|
+
|
|
264
|
+
# Process the show
|
|
265
|
+
from mkv_episode_matcher.episode_matcher import process_show
|
|
266
|
+
from mkv_episode_matcher.utils import get_valid_seasons
|
|
267
|
+
|
|
268
|
+
console.print()
|
|
269
|
+
if args.dry_run:
|
|
270
|
+
console.print(
|
|
271
|
+
Panel.fit(
|
|
272
|
+
"[bold yellow]DRY RUN MODE[/bold yellow]\n"
|
|
273
|
+
"Files will not be renamed, only showing what would happen.",
|
|
274
|
+
border_style="yellow",
|
|
275
|
+
)
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
seasons = get_valid_seasons(show_dir)
|
|
279
|
+
if not seasons:
|
|
280
|
+
console.print("[bold red]Error:[/bold red] No seasons with .mkv files found in the show directory.")
|
|
281
|
+
return
|
|
282
|
+
|
|
283
|
+
# If season wasn't specified and there are multiple seasons, let user choose
|
|
284
|
+
selected_season = args.season
|
|
285
|
+
if selected_season is None and len(seasons) > 1:
|
|
286
|
+
selected_season = select_season(seasons)
|
|
287
|
+
|
|
288
|
+
# Show what's going to happen
|
|
289
|
+
show_name = os.path.basename(show_dir)
|
|
290
|
+
season_text = f"Season {selected_season}" if selected_season else "all seasons"
|
|
291
|
+
|
|
292
|
+
console.print(
|
|
293
|
+
f"[bold green]Processing[/bold green] [cyan]{show_name}[/cyan], {season_text}"
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
# # Setup progress spinner
|
|
297
|
+
# with Progress(
|
|
298
|
+
# TextColumn("[bold green]Processing...[/bold green]"),
|
|
299
|
+
# console=console,
|
|
300
|
+
# ) as progress:
|
|
301
|
+
# task = progress.add_task("", total=None)
|
|
302
|
+
process_show(
|
|
303
|
+
selected_season,
|
|
304
|
+
dry_run=args.dry_run,
|
|
305
|
+
get_subs=args.get_subs,
|
|
306
|
+
verbose=args.verbose,
|
|
307
|
+
confidence=args.confidence
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
console.print("[bold green]✓[/bold green] Processing completed successfully!")
|
|
311
|
+
|
|
312
|
+
# Show where logs are stored
|
|
313
|
+
console.print(f"\n[dim]Logs available at: {log_dir}[/dim]")
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
# Run the main function if the script is run directly
|
|
317
|
+
if __name__ == "__main__":
|
|
318
|
+
try:
|
|
319
|
+
main()
|
|
320
|
+
except KeyboardInterrupt:
|
|
321
|
+
console.print("\n[yellow]Process interrupted by user.[/yellow]")
|
|
322
|
+
sys.exit(1)
|
|
323
|
+
except Exception as e:
|
|
324
|
+
console.print(f"\n[bold red]Error:[/bold red] {str(e)}")
|
|
325
|
+
logger.exception("Unhandled exception")
|
|
326
|
+
sys.exit(1)
|