mkv-episode-matcher 0.9.2__tar.gz → 0.9.4__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.9.2 → mkv_episode_matcher-0.9.4}/.coverage +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/CHANGELOG.md +12 -0
- mkv_episode_matcher-0.9.4/LICENSE +21 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/PKG-INFO +56 -11
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/README.md +31 -9
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/docs/configuration.md +0 -19
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/docs/quickstart.md +24 -5
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/mkv_episode_matcher/__main__.py +90 -48
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/mkv_episode_matcher/episode_identification.py +59 -34
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/mkv_episode_matcher/episode_matcher.py +50 -28
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/mkv_episode_matcher/utils.py +54 -37
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/mkv_episode_matcher.egg-info/PKG-INFO +56 -11
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/mkv_episode_matcher.egg-info/SOURCES.txt +1 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/mkv_episode_matcher.egg-info/requires.txt +1 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/pyproject.toml +2 -2
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/setup.cfg +1 -1
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/tests/test_main.py +4 -25
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/tests/test_path_handling.py +58 -36
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/tests/test_trailing_slash.py +31 -11
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/uv.lock +1174 -1174
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/.gitattributes +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/.github/funding.yml +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/.github/workflows/documentation.yml +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/.github/workflows/python-publish.yml +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/.github/workflows/tests.yml +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/.gitignore +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/.gitmodules +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/.python-version +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/.vscode/settings.json +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/docs/api/index.md +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/docs/changelog.md +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/docs/cli.md +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/docs/installation.md +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/docs/tips.md +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/mkdocs.yml +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/mkv_episode_matcher/.gitattributes +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/mkv_episode_matcher/__init__.py +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/mkv_episode_matcher/config.py +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/mkv_episode_matcher/subtitle_utils.py +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/mkv_episode_matcher/tmdb_client.py +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/mkv_episode_matcher.egg-info/dependency_links.txt +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/mkv_episode_matcher.egg-info/entry_points.txt +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/mkv_episode_matcher.egg-info/top_level.txt +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/setup.py +0 -0
- {mkv_episode_matcher-0.9.2 → mkv_episode_matcher-0.9.4}/tests/__init__.py +0 -0
|
Binary file
|
|
@@ -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
|
+
Version: 0.9.4
|
|
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
|
|
|
@@ -45,15 +68,11 @@ Automatically match and rename your MKV TV episodes using The Movie Database (TM
|
|
|
45
68
|
|
|
46
69
|
## Features
|
|
47
70
|
|
|
48
|
-
- 🎯 **Automatic Episode Matching**: Uses TMDb to accurately identify episodes
|
|
49
|
-
-
|
|
50
|
-
- 📝 **Subtitle Extraction**: Extracts subtitles from MKV files
|
|
51
|
-
- 🔊 **Speech Recognition**: Uses Whisper for accurate episode identification
|
|
52
|
-
- 🚀 **Multi-threaded**: Fast processing of multiple files
|
|
71
|
+
- 🎯 **Automatic Episode Matching**: Uses TMDb and OpenSubtitles to accurately identify episodes
|
|
72
|
+
- 🔊 **Speech Recognition**: Uses OpenAI Whisper for accurate episode identification
|
|
53
73
|
- ⬇️ **Subtitle Downloads**: Integration with OpenSubtitles
|
|
54
74
|
- ✨ **Bulk Processing**: Handle entire seasons at once
|
|
55
75
|
- 🧪 **Dry Run Mode**: Test changes before applying
|
|
56
|
-
- 🎮 **Interactive Mode**: User-friendly season selection and configuration
|
|
57
76
|
|
|
58
77
|
## Prerequisites
|
|
59
78
|
|
|
@@ -66,15 +85,41 @@ Automatically match and rename your MKV TV episodes using The Movie Database (TM
|
|
|
66
85
|
|
|
67
86
|
1. Install the package:
|
|
68
87
|
```bash
|
|
69
|
-
pip install mkv-episode-matcher
|
|
88
|
+
pip install -U mkv-episode-matcher
|
|
70
89
|
```
|
|
71
|
-
2.
|
|
90
|
+
2. Run onboarding to set up your configuration (first-time users or to update credentials):
|
|
91
|
+
```bash
|
|
92
|
+
mkv-match --onboard
|
|
93
|
+
```
|
|
94
|
+
- You will be prompted for:
|
|
95
|
+
- TMDb API key (for episode matching)
|
|
96
|
+
- OpenSubtitles API key, Consumer Name, Username, and Password (for subtitle downloads)
|
|
97
|
+
- Show Directory (main directory of your show)
|
|
98
|
+
- If a config value already exists, you can accept the default or enter a new value.
|
|
72
99
|
|
|
73
|
-
3.
|
|
100
|
+
3.
|
|
101
|
+
a. If you setup the TMDb and Opensubtitles credentials above, automatically fetch subtitles with the `--get-subs` flag.
|
|
102
|
+
b. Alternatively, manually download .srt subtitles files to ~/.mkv-episode-matcher/cache/data/Show Name/
|
|
103
|
+
|
|
104
|
+
4. Run on your show directory:
|
|
74
105
|
```bash
|
|
75
106
|
mkv-match --show-dir "path/to/your/show"
|
|
76
107
|
```
|
|
77
108
|
|
|
109
|
+
## Onboarding & Configuration
|
|
110
|
+
|
|
111
|
+
The onboarding process will prompt you for all required configuration values if you run with `--onboard` or if no config file exists. You can re-run onboarding at any time to update your credentials or show directory.
|
|
112
|
+
|
|
113
|
+
**Required information:**
|
|
114
|
+
- TMDb API key (for episode matching)
|
|
115
|
+
- OpenSubtitles API key (for subtitle downloads)
|
|
116
|
+
- OpenSubtitles Consumer Name (for subtitle downloads)
|
|
117
|
+
- OpenSubtitles Username (for subtitle downloads)
|
|
118
|
+
- OpenSubtitles Password (for subtitle downloads)
|
|
119
|
+
- Show Directory (main directory of your show)
|
|
120
|
+
|
|
121
|
+
If a value already exists, it will be shown as the default and you can accept it or enter a new value.
|
|
122
|
+
|
|
78
123
|
## Directory Structure
|
|
79
124
|
|
|
80
125
|
MKV Episode Matcher expects your TV shows to be organized as follows:
|
|
@@ -14,15 +14,11 @@ Automatically match and rename your MKV TV episodes using The Movie Database (TM
|
|
|
14
14
|
|
|
15
15
|
## Features
|
|
16
16
|
|
|
17
|
-
- 🎯 **Automatic Episode Matching**: Uses TMDb to accurately identify episodes
|
|
18
|
-
-
|
|
19
|
-
- 📝 **Subtitle Extraction**: Extracts subtitles from MKV files
|
|
20
|
-
- 🔊 **Speech Recognition**: Uses Whisper for accurate episode identification
|
|
21
|
-
- 🚀 **Multi-threaded**: Fast processing of multiple files
|
|
17
|
+
- 🎯 **Automatic Episode Matching**: Uses TMDb and OpenSubtitles to accurately identify episodes
|
|
18
|
+
- 🔊 **Speech Recognition**: Uses OpenAI Whisper for accurate episode identification
|
|
22
19
|
- ⬇️ **Subtitle Downloads**: Integration with OpenSubtitles
|
|
23
20
|
- ✨ **Bulk Processing**: Handle entire seasons at once
|
|
24
21
|
- 🧪 **Dry Run Mode**: Test changes before applying
|
|
25
|
-
- 🎮 **Interactive Mode**: User-friendly season selection and configuration
|
|
26
22
|
|
|
27
23
|
## Prerequisites
|
|
28
24
|
|
|
@@ -35,15 +31,41 @@ Automatically match and rename your MKV TV episodes using The Movie Database (TM
|
|
|
35
31
|
|
|
36
32
|
1. Install the package:
|
|
37
33
|
```bash
|
|
38
|
-
pip install mkv-episode-matcher
|
|
34
|
+
pip install -U mkv-episode-matcher
|
|
39
35
|
```
|
|
40
|
-
2.
|
|
36
|
+
2. Run onboarding to set up your configuration (first-time users or to update credentials):
|
|
37
|
+
```bash
|
|
38
|
+
mkv-match --onboard
|
|
39
|
+
```
|
|
40
|
+
- You will be prompted for:
|
|
41
|
+
- TMDb API key (for episode matching)
|
|
42
|
+
- OpenSubtitles API key, Consumer Name, Username, and Password (for subtitle downloads)
|
|
43
|
+
- Show Directory (main directory of your show)
|
|
44
|
+
- If a config value already exists, you can accept the default or enter a new value.
|
|
41
45
|
|
|
42
|
-
3.
|
|
46
|
+
3.
|
|
47
|
+
a. If you setup the TMDb and Opensubtitles credentials above, automatically fetch subtitles with the `--get-subs` flag.
|
|
48
|
+
b. Alternatively, manually download .srt subtitles files to ~/.mkv-episode-matcher/cache/data/Show Name/
|
|
49
|
+
|
|
50
|
+
4. Run on your show directory:
|
|
43
51
|
```bash
|
|
44
52
|
mkv-match --show-dir "path/to/your/show"
|
|
45
53
|
```
|
|
46
54
|
|
|
55
|
+
## Onboarding & Configuration
|
|
56
|
+
|
|
57
|
+
The onboarding process will prompt you for all required configuration values if you run with `--onboard` or if no config file exists. You can re-run onboarding at any time to update your credentials or show directory.
|
|
58
|
+
|
|
59
|
+
**Required information:**
|
|
60
|
+
- TMDb API key (for episode matching)
|
|
61
|
+
- OpenSubtitles API key (for subtitle downloads)
|
|
62
|
+
- OpenSubtitles Consumer Name (for subtitle downloads)
|
|
63
|
+
- OpenSubtitles Username (for subtitle downloads)
|
|
64
|
+
- OpenSubtitles Password (for subtitle downloads)
|
|
65
|
+
- Show Directory (main directory of your show)
|
|
66
|
+
|
|
67
|
+
If a value already exists, it will be shown as the default and you can accept it or enter a new value.
|
|
68
|
+
|
|
47
69
|
## Directory Structure
|
|
48
70
|
|
|
49
71
|
MKV Episode Matcher expects your TV shows to be organized as follows:
|
|
@@ -36,25 +36,6 @@ mkv-match \
|
|
|
36
36
|
--get-subs true
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
## Environment Variables
|
|
40
|
-
|
|
41
|
-
You can also use environment variables:
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
export TMDB_API_KEY="your_key"
|
|
45
|
-
export SHOW_DIR="/path/to/shows"
|
|
46
|
-
export OPEN_SUBTITLES_API_KEY="your_key"
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Configuration Priority
|
|
50
|
-
|
|
51
|
-
Settings are loaded in the following order (later overrides earlier):
|
|
52
|
-
|
|
53
|
-
1. Default values
|
|
54
|
-
2. Configuration file
|
|
55
|
-
3. Environment variables
|
|
56
|
-
4. Command line arguments
|
|
57
|
-
|
|
58
39
|
## Detailed Options
|
|
59
40
|
|
|
60
41
|
### TMDb Configuration
|
|
@@ -4,22 +4,36 @@ Get started with MKV Episode Matcher quickly and efficiently.
|
|
|
4
4
|
|
|
5
5
|
## Basic Usage
|
|
6
6
|
|
|
7
|
-
### 1.
|
|
7
|
+
### 1. Onboarding (First-Time Setup)
|
|
8
|
+
|
|
9
|
+
Before running any matching, set up your configuration:
|
|
10
|
+
```bash
|
|
11
|
+
mkv-match --onboard
|
|
12
|
+
```
|
|
13
|
+
You will be prompted for:
|
|
14
|
+
- TMDb API key (for episode matching)
|
|
15
|
+
- OpenSubtitles API key, Consumer Name, Username, and Password (for subtitle downloads)
|
|
16
|
+
- Show Directory (main directory of your show)
|
|
17
|
+
If a value already exists, you can accept the default or enter a new value.
|
|
18
|
+
|
|
19
|
+
You can re-run onboarding at any time to update your credentials or show directory.
|
|
20
|
+
|
|
21
|
+
### 2. Interactive Mode
|
|
8
22
|
|
|
9
23
|
Simply run:
|
|
10
24
|
```bash
|
|
11
25
|
mkv-match
|
|
12
26
|
```
|
|
13
|
-
The program will guide you through the setup interactively.
|
|
27
|
+
The program will guide you through the setup interactively if configuration is missing.
|
|
14
28
|
|
|
15
|
-
###
|
|
29
|
+
### 3. Command Line Options
|
|
16
30
|
|
|
17
31
|
Process a specific season:
|
|
18
32
|
```bash
|
|
19
33
|
mkv-match --show-dir "/path/to/show" --season 1
|
|
20
34
|
```
|
|
21
35
|
|
|
22
|
-
Process all seasons with subtitles:
|
|
36
|
+
Process all seasons with subtitles (requires onboarding):
|
|
23
37
|
```bash
|
|
24
38
|
mkv-match --show-dir "/path/to/show" --get-subs
|
|
25
39
|
```
|
|
@@ -53,7 +67,12 @@ Show Name/
|
|
|
53
67
|
|
|
54
68
|
## Configuration
|
|
55
69
|
|
|
56
|
-
Configuration is stored at `~/.mkv-episode-matcher/config.ini
|
|
70
|
+
Configuration is stored at `~/.mkv-episode-matcher/config.ini` and can be set up or updated at any time with:
|
|
71
|
+
```bash
|
|
72
|
+
mkv-match --onboard
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Example config:
|
|
57
76
|
```ini
|
|
58
77
|
[Config]
|
|
59
78
|
tmdb_api_key = your_tmdb_api_key
|
|
@@ -7,7 +7,6 @@ from typing import Optional
|
|
|
7
7
|
from loguru import logger
|
|
8
8
|
from rich.console import Console
|
|
9
9
|
from rich.panel import Panel
|
|
10
|
-
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
11
10
|
from rich.prompt import Confirm, Prompt
|
|
12
11
|
|
|
13
12
|
from mkv_episode_matcher import __version__
|
|
@@ -62,15 +61,17 @@ def print_welcome_message():
|
|
|
62
61
|
console.print()
|
|
63
62
|
|
|
64
63
|
|
|
65
|
-
def confirm_api_key(
|
|
64
|
+
def confirm_api_key(
|
|
65
|
+
config_value: Optional[str], key_name: str, description: str
|
|
66
|
+
) -> str:
|
|
66
67
|
"""
|
|
67
68
|
Confirm if the user wants to use an existing API key or enter a new one.
|
|
68
|
-
|
|
69
|
+
|
|
69
70
|
Args:
|
|
70
71
|
config_value: The current value from the config
|
|
71
72
|
key_name: The name of the key
|
|
72
73
|
description: Description of the key for user information
|
|
73
|
-
|
|
74
|
+
|
|
74
75
|
Returns:
|
|
75
76
|
The API key to use
|
|
76
77
|
"""
|
|
@@ -79,7 +80,7 @@ def confirm_api_key(config_value: Optional[str], key_name: str, description: str
|
|
|
79
80
|
console.print(f"Current value: [green]{mask_api_key(config_value)}[/green]")
|
|
80
81
|
if Confirm.ask("Use existing key?", default=True):
|
|
81
82
|
return config_value
|
|
82
|
-
|
|
83
|
+
|
|
83
84
|
return Prompt.ask(f"Enter your {key_name}")
|
|
84
85
|
|
|
85
86
|
|
|
@@ -95,10 +96,10 @@ def mask_api_key(key: str) -> str:
|
|
|
95
96
|
def select_season(seasons):
|
|
96
97
|
"""
|
|
97
98
|
Allow user to select a season from a list.
|
|
98
|
-
|
|
99
|
+
|
|
99
100
|
Args:
|
|
100
101
|
seasons: List of available seasons
|
|
101
|
-
|
|
102
|
+
|
|
102
103
|
Returns:
|
|
103
104
|
Selected season number or None for all seasons
|
|
104
105
|
"""
|
|
@@ -106,21 +107,51 @@ def select_season(seasons):
|
|
|
106
107
|
for i, season in enumerate(seasons, 1):
|
|
107
108
|
season_num = Path(season).name.replace("Season ", "")
|
|
108
109
|
console.print(f" {i}. Season {season_num}")
|
|
109
|
-
|
|
110
|
-
console.print(
|
|
111
|
-
|
|
110
|
+
|
|
111
|
+
console.print(" 0. All Seasons")
|
|
112
|
+
|
|
112
113
|
choice = Prompt.ask(
|
|
113
114
|
"Select a season number (0 for all)",
|
|
114
115
|
choices=[str(i) for i in range(len(seasons) + 1)],
|
|
115
|
-
default="0"
|
|
116
|
+
default="0",
|
|
116
117
|
)
|
|
117
|
-
|
|
118
|
+
|
|
118
119
|
if int(choice) == 0:
|
|
119
120
|
return None
|
|
120
|
-
|
|
121
|
+
|
|
121
122
|
selected_season = seasons[int(choice) - 1]
|
|
122
123
|
return int(Path(selected_season).name.replace("Season ", ""))
|
|
123
124
|
|
|
125
|
+
def onboarding(config_path):
|
|
126
|
+
"""Prompt user for all required config values, showing existing as defaults."""
|
|
127
|
+
config = get_config(config_path) if config_path.exists() else {}
|
|
128
|
+
|
|
129
|
+
def ask_with_default(prompt_text, key, description, secret=False):
|
|
130
|
+
current = config.get(key)
|
|
131
|
+
if current:
|
|
132
|
+
console.print(f"[cyan]{key}:[/cyan] {description}")
|
|
133
|
+
console.print(f"Current value: [green]{mask_api_key(current) if secret else current}[/green]")
|
|
134
|
+
if Confirm.ask("Use existing value?", default=True):
|
|
135
|
+
return current
|
|
136
|
+
return Prompt.ask(f"Enter your {key}", default=current or "")
|
|
137
|
+
|
|
138
|
+
tmdb_api_key = ask_with_default("TMDb API key", "tmdb_api_key", "Used to lookup show and episode information. To get your API key, create an account at https://www.themoviedb.org/ and follow the instructions at https://developer.themoviedb.org/docs/getting-started", secret=True)
|
|
139
|
+
open_subtitles_username = ask_with_default("OpenSubtitles Username", "open_subtitles_username", "Account username for OpenSubtitles. To create an account, visit https://www.opensubtitles.com/ then click 'Register'")
|
|
140
|
+
open_subtitles_password = ask_with_default("OpenSubtitles Password", "open_subtitles_password", "Account password for OpenSubtitles", secret=True)
|
|
141
|
+
open_subtitles_user_agent = ask_with_default("OpenSubtitles Consumer Name", "open_subtitles_user_agent", "Required for subtitle downloads. Go to https://www.opensubtitles.com/en/consumers, click 'New Consumer', give it a name, then click 'Save'")
|
|
142
|
+
open_subtitles_api_key = ask_with_default("OpenSubtitles API key", "open_subtitles_api_key", "Required for subtitle downloads. Enter the API key linked with the OpenSubtitles Consumer that you created in the previous step.", secret=True)
|
|
143
|
+
show_dir = ask_with_default("Show Directory", "show_dir", "Main directory of the show")
|
|
144
|
+
|
|
145
|
+
set_config(
|
|
146
|
+
tmdb_api_key,
|
|
147
|
+
open_subtitles_api_key,
|
|
148
|
+
open_subtitles_user_agent,
|
|
149
|
+
open_subtitles_username,
|
|
150
|
+
open_subtitles_password,
|
|
151
|
+
show_dir,
|
|
152
|
+
config_path,
|
|
153
|
+
)
|
|
154
|
+
console.print("[bold green]Onboarding complete! Configuration saved.[/bold green]")
|
|
124
155
|
|
|
125
156
|
@logger.catch
|
|
126
157
|
def main():
|
|
@@ -165,7 +196,8 @@ def main():
|
|
|
165
196
|
help="Check if GPU is available for faster processing",
|
|
166
197
|
)
|
|
167
198
|
parser.add_argument(
|
|
168
|
-
"--verbose",
|
|
199
|
+
"--verbose",
|
|
200
|
+
"-v",
|
|
169
201
|
action="store_true",
|
|
170
202
|
help="Enable verbose output",
|
|
171
203
|
)
|
|
@@ -175,22 +207,30 @@ def main():
|
|
|
175
207
|
default=0.7,
|
|
176
208
|
help="Set confidence threshold for episode matching (0.0-1.0)",
|
|
177
209
|
)
|
|
178
|
-
|
|
210
|
+
parser.add_argument(
|
|
211
|
+
"--onboard",
|
|
212
|
+
action="store_true",
|
|
213
|
+
help="Run onboarding to set up configuration",
|
|
214
|
+
)
|
|
179
215
|
args = parser.parse_args()
|
|
180
216
|
if args.verbose:
|
|
181
217
|
console.print("[bold cyan]Command-line Arguments[/bold cyan]")
|
|
182
218
|
console.print(args)
|
|
183
219
|
if args.check_gpu:
|
|
184
220
|
from mkv_episode_matcher.utils import check_gpu_support
|
|
221
|
+
|
|
185
222
|
with console.status("[bold green]Checking GPU support..."):
|
|
186
223
|
check_gpu_support()
|
|
187
224
|
return
|
|
188
225
|
|
|
189
|
-
|
|
190
226
|
logger.debug(f"Command-line arguments: {args}")
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
227
|
+
# Onboarding: run if --onboard or config file missing
|
|
228
|
+
if args.onboard or not CONFIG_FILE.exists():
|
|
229
|
+
onboarding(CONFIG_FILE)
|
|
230
|
+
# Reload config after onboarding
|
|
231
|
+
config = get_config(CONFIG_FILE)
|
|
232
|
+
else:
|
|
233
|
+
config = get_config(CONFIG_FILE)
|
|
194
234
|
|
|
195
235
|
# Get TMDb API key
|
|
196
236
|
tmdb_api_key = args.tmdb_api_key or config.get("tmdb_api_key")
|
|
@@ -202,49 +242,49 @@ def main():
|
|
|
202
242
|
|
|
203
243
|
if args.get_subs:
|
|
204
244
|
console.print("[bold cyan]Subtitle Download Configuration[/bold cyan]")
|
|
205
|
-
|
|
245
|
+
|
|
206
246
|
tmdb_api_key = confirm_api_key(
|
|
207
|
-
tmdb_api_key,
|
|
208
|
-
"TMDb API key",
|
|
209
|
-
"Used to lookup show and episode information"
|
|
247
|
+
tmdb_api_key, "TMDb API key", "Used to lookup show and episode information"
|
|
210
248
|
)
|
|
211
|
-
|
|
249
|
+
|
|
212
250
|
open_subtitles_api_key = confirm_api_key(
|
|
213
251
|
open_subtitles_api_key,
|
|
214
252
|
"OpenSubtitles API key",
|
|
215
|
-
"Required for subtitle downloads"
|
|
253
|
+
"Required for subtitle downloads",
|
|
216
254
|
)
|
|
217
|
-
|
|
255
|
+
|
|
218
256
|
open_subtitles_user_agent = confirm_api_key(
|
|
219
257
|
open_subtitles_user_agent,
|
|
220
258
|
"OpenSubtitles User Agent",
|
|
221
|
-
"Required for subtitle downloads"
|
|
259
|
+
"Required for subtitle downloads",
|
|
222
260
|
)
|
|
223
|
-
|
|
261
|
+
|
|
224
262
|
open_subtitles_username = confirm_api_key(
|
|
225
263
|
open_subtitles_username,
|
|
226
264
|
"OpenSubtitles Username",
|
|
227
|
-
"Account username for OpenSubtitles"
|
|
265
|
+
"Account username for OpenSubtitles",
|
|
228
266
|
)
|
|
229
|
-
|
|
267
|
+
|
|
230
268
|
open_subtitles_password = confirm_api_key(
|
|
231
269
|
open_subtitles_password,
|
|
232
270
|
"OpenSubtitles Password",
|
|
233
|
-
"Account password for OpenSubtitles"
|
|
271
|
+
"Account password for OpenSubtitles",
|
|
234
272
|
)
|
|
235
273
|
|
|
236
274
|
# Use config for show directory
|
|
237
275
|
show_dir = args.show_dir or config.get("show_dir")
|
|
238
276
|
if not show_dir:
|
|
239
277
|
show_dir = Prompt.ask("Enter the main directory of the show")
|
|
240
|
-
|
|
278
|
+
|
|
241
279
|
logger.info(f"Show Directory: {show_dir}")
|
|
242
280
|
if not Path(show_dir).exists():
|
|
243
|
-
console.print(
|
|
281
|
+
console.print(
|
|
282
|
+
f"[bold red]Error:[/bold red] Show directory '{show_dir}' does not exist."
|
|
283
|
+
)
|
|
244
284
|
return
|
|
245
|
-
|
|
285
|
+
|
|
246
286
|
if not show_dir:
|
|
247
|
-
show_dir =
|
|
287
|
+
show_dir = Path.cwd()
|
|
248
288
|
console.print(f"Using current directory: [cyan]{show_dir}[/cyan]")
|
|
249
289
|
|
|
250
290
|
logger.debug(f"Show Directory: {show_dir}")
|
|
@@ -274,25 +314,27 @@ def main():
|
|
|
274
314
|
border_style="yellow",
|
|
275
315
|
)
|
|
276
316
|
)
|
|
277
|
-
|
|
317
|
+
|
|
278
318
|
seasons = get_valid_seasons(show_dir)
|
|
279
319
|
if not seasons:
|
|
280
|
-
console.print(
|
|
320
|
+
console.print(
|
|
321
|
+
"[bold red]Error:[/bold red] No seasons with .mkv files found in the show directory."
|
|
322
|
+
)
|
|
281
323
|
return
|
|
282
|
-
|
|
324
|
+
|
|
283
325
|
# If season wasn't specified and there are multiple seasons, let user choose
|
|
284
326
|
selected_season = args.season
|
|
285
327
|
if selected_season is None and len(seasons) > 1:
|
|
286
328
|
selected_season = select_season(seasons)
|
|
287
|
-
|
|
329
|
+
|
|
288
330
|
# Show what's going to happen
|
|
289
331
|
show_name = Path(show_dir).name
|
|
290
332
|
season_text = f"Season {selected_season}" if selected_season else "all seasons"
|
|
291
|
-
|
|
333
|
+
|
|
292
334
|
console.print(
|
|
293
335
|
f"[bold green]Processing[/bold green] [cyan]{show_name}[/cyan], {season_text}"
|
|
294
336
|
)
|
|
295
|
-
|
|
337
|
+
|
|
296
338
|
# # Setup progress spinner
|
|
297
339
|
# with Progress(
|
|
298
340
|
# TextColumn("[bold green]Processing...[/bold green]"),
|
|
@@ -300,15 +342,15 @@ def main():
|
|
|
300
342
|
# ) as progress:
|
|
301
343
|
# task = progress.add_task("", total=None)
|
|
302
344
|
process_show(
|
|
303
|
-
selected_season,
|
|
304
|
-
dry_run=args.dry_run,
|
|
305
|
-
get_subs=args.get_subs,
|
|
345
|
+
selected_season,
|
|
346
|
+
dry_run=args.dry_run,
|
|
347
|
+
get_subs=args.get_subs,
|
|
306
348
|
verbose=args.verbose,
|
|
307
|
-
confidence=args.confidence
|
|
349
|
+
confidence=args.confidence,
|
|
308
350
|
)
|
|
309
|
-
|
|
351
|
+
|
|
310
352
|
console.print("[bold green]✓[/bold green] Processing completed successfully!")
|
|
311
|
-
|
|
353
|
+
|
|
312
354
|
# Show where logs are stored
|
|
313
355
|
console.print(f"\n[dim]Logs available at: {log_dir}[/dim]")
|
|
314
356
|
|
|
@@ -323,4 +365,4 @@ if __name__ == "__main__":
|
|
|
323
365
|
except Exception as e:
|
|
324
366
|
console.print(f"\n[bold red]Error:[/bold red] {str(e)}")
|
|
325
367
|
logger.exception("Unhandled exception")
|
|
326
|
-
sys.exit(1)
|
|
368
|
+
sys.exit(1)
|