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.

Files changed (55) hide show
  1. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/.coverage +0 -0
  2. mkv_episode_matcher-0.7.0/CHANGELOG.md +51 -0
  3. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/PKG-INFO +10 -13
  4. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/README.md +8 -3
  5. mkv_episode_matcher-0.7.0/docs/changelog.md +36 -0
  6. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/docs/cli.md +7 -8
  7. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/docs/configuration.md +1 -16
  8. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/docs/installation.md +2 -8
  9. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/docs/tips.md +14 -3
  10. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher/__init__.py +2 -2
  11. mkv_episode_matcher-0.7.0/mkv_episode_matcher/__main__.py +326 -0
  12. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher/config.py +0 -3
  13. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher/episode_identification.py +164 -124
  14. mkv_episode_matcher-0.7.0/mkv_episode_matcher/episode_matcher.py +152 -0
  15. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher/subtitle_utils.py +26 -25
  16. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher/utils.py +74 -57
  17. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher.egg-info/PKG-INFO +10 -13
  18. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher.egg-info/SOURCES.txt +2 -11
  19. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher.egg-info/requires.txt +1 -11
  20. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/pyproject.toml +1 -45
  21. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/setup.cfg +1 -1
  22. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/tests/test_main.py +19 -29
  23. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/uv.lock +555 -534
  24. mkv_episode_matcher-0.5.0/mkv_episode_matcher/__main__.py +0 -180
  25. mkv_episode_matcher-0.5.0/mkv_episode_matcher/episode_matcher.py +0 -105
  26. mkv_episode_matcher-0.5.0/mkv_episode_matcher/libraries/pgs2srt/.gitignore +0 -2
  27. mkv_episode_matcher-0.5.0/mkv_episode_matcher/libraries/pgs2srt/Libraries/SubZero/SubZero.py +0 -321
  28. mkv_episode_matcher-0.5.0/mkv_episode_matcher/libraries/pgs2srt/Libraries/SubZero/dictionaries/data.py +0 -16700
  29. mkv_episode_matcher-0.5.0/mkv_episode_matcher/libraries/pgs2srt/Libraries/SubZero/post_processing.py +0 -260
  30. mkv_episode_matcher-0.5.0/mkv_episode_matcher/libraries/pgs2srt/README.md +0 -26
  31. mkv_episode_matcher-0.5.0/mkv_episode_matcher/libraries/pgs2srt/imagemaker.py +0 -89
  32. mkv_episode_matcher-0.5.0/mkv_episode_matcher/libraries/pgs2srt/pgs2srt.py +0 -150
  33. mkv_episode_matcher-0.5.0/mkv_episode_matcher/libraries/pgs2srt/pgsreader.py +0 -225
  34. mkv_episode_matcher-0.5.0/mkv_episode_matcher/libraries/pgs2srt/requirements.txt +0 -4
  35. mkv_episode_matcher-0.5.0/mkv_episode_matcher/mkv_to_srt.py +0 -302
  36. mkv_episode_matcher-0.5.0/tests/__init__.py +0 -0
  37. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/.gitattributes +0 -0
  38. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/.github/funding.yml +0 -0
  39. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/.github/workflows/documentation.yml +0 -0
  40. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/.github/workflows/python-publish.yml +0 -0
  41. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/.github/workflows/tests.yml +0 -0
  42. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/.gitignore +0 -0
  43. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/.gitmodules +0 -0
  44. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/.python-version +0 -0
  45. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/.vscode/settings.json +0 -0
  46. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/docs/api/index.md +0 -0
  47. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/docs/quickstart.md +0 -0
  48. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkdocs.yml +0 -0
  49. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher/.gitattributes +0 -0
  50. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher/tmdb_client.py +0 -0
  51. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher.egg-info/dependency_links.txt +0 -0
  52. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher.egg-info/entry_points.txt +0 -0
  53. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/mkv_episode_matcher.egg-info/top_level.txt +0 -0
  54. {mkv_episode_matcher-0.5.0 → mkv_episode_matcher-0.7.0}/setup.py +0 -0
  55. {mkv_episode_matcher-0.5.0/mkv_episode_matcher/libraries/pgs2srt → mkv_episode_matcher-0.7.0/tests}/__init__.py +0 -0
@@ -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.5.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
  [![GitHub last commit](https://img.shields.io/github/last-commit/Jsakkos/mkv-episode-matcher)](https://github.com/Jsakkos/mkv-episode-matcher/commits/main)
49
41
  [![GitHub issues](https://img.shields.io/github/issues/Jsakkos/mkv-episode-matcher)](https://github.com/Jsakkos/mkv-episode-matcher/issues)
50
42
  [![Tests](https://github.com/Jsakkos/mkv-episode-matcher/actions/workflows/tests.yml/badge.svg)](https://github.com/Jsakkos/mkv-episode-matcher/actions/workflows/tests.yml)
51
- [![codecov](https://codecov.io/gh/Jsakkos/mkv-episode-matcher/branch/main/graph/badge.svg)](https://codecov.io/gh/Jsakkos/mkv-episode-matcher)
43
+ [![codecov](https://codecov.io/gh/Jsakkos/mkv-episode-matcher/branch/main/graph/badge.svg)](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
- - 🔍 **OCR Support**: Handles image-based subtitles
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
  [![GitHub last commit](https://img.shields.io/github/last-commit/Jsakkos/mkv-episode-matcher)](https://github.com/Jsakkos/mkv-episode-matcher/commits/main)
9
9
  [![GitHub issues](https://img.shields.io/github/issues/Jsakkos/mkv-episode-matcher)](https://github.com/Jsakkos/mkv-episode-matcher/issues)
10
10
  [![Tests](https://github.com/Jsakkos/mkv-episode-matcher/actions/workflows/tests.yml/badge.svg)](https://github.com/Jsakkos/mkv-episode-matcher/actions/workflows/tests.yml)
11
- [![codecov](https://codecov.io/gh/Jsakkos/mkv-episode-matcher/branch/main/graph/badge.svg)](https://codecov.io/gh/Jsakkos/mkv-episode-matcher)
11
+ [![codecov](https://codecov.io/gh/Jsakkos/mkv-episode-matcher/branch/main/graph/badge.svg)](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
- - 🔍 **OCR Support**: Handles image-based subtitles
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 | 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 |
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
- 2. **Tesseract OCR**
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 cores
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 cleanup_ocr_files
101
- cleanup_ocr_files(show_dir)
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
- from importlib.metadata import version, PackageNotFoundError
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)