Skip to content

Commit a101466

Browse files
Bump version to 2.47.2 (#777)
2 parents 836a0cb + 7dd3b8c commit a101466

19 files changed

Lines changed: 220 additions & 178 deletions
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"current_version": "2.47.1",
3-
"previous_version": "2.47.1dev0",
4-
"last_stable_release": "2.47.1"
2+
"current_version": "2.47.2",
3+
"previous_version": "2.47.2dev4",
4+
"last_stable_release": "2.47.2"
55
}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,3 +173,5 @@ artifacts
173173
artifacts/*
174174
.GITHUB_OUTPUT
175175
.GITHUB_STEP_SUMMARY
176+
cspell.json
177+
cspell.config.yaml

pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,15 @@
7070
[project.optional-dependencies]
7171
build = [
7272
"build==1.2.2.post1",
73-
"cx_Freeze==8.0.0",
73+
"cx_Freeze==8.1.0",
7474
"twine==6.1.0",
7575
"psutil==7.0.0",
7676
]
7777
lint = ["black==25.1.0", "isort==6.0.1"]
7878
tests = [
79-
"pytest-cov==6.0.0",
79+
"pytest-cov==6.1.1",
8080
"pytest==8.3.5",
81-
"tox==4.24.2",
81+
"tox==4.25.0",
8282
"python-dotenv==1.1.0",
8383
]
8484
tools = ["pyperclip==1.9.0"]

src/subsearch/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ def start_app(self) -> None:
2424
)
2525
self.subsearch_core.download_files()
2626
self.subsearch_core.download_manager()
27-
self.subsearch_core.extract_files()
2827
self.subsearch_core.subtitle_post_processing()
2928
self.subsearch_core.clean_up()
3029

src/subsearch/core.py

Lines changed: 60 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ def __init__(self, pref_counter: float) -> None:
1818
log.brackets("Initializing")
1919

2020
self.api_calls_made: dict[str, int] = {}
21-
self.downloaded_subtitles = 0
2221
self.ran_download_tab = False
2322
self.accepted_subtitles: list[Subtitle] = []
2423
self.rejected_subtitles: list[Subtitle] = []
@@ -28,6 +27,11 @@ def __init__(self, pref_counter: float) -> None:
2827
self.file_exist = VIDEO_FILE.file_exist
2928
self.autoload_src = ""
3029

30+
self.downloaded_subtitles: int = 0
31+
self.downloaded_subtitle_archives: int = 0
32+
self.extracted_subtitle_archives: int = 0
33+
self.user_downloaded_files = False
34+
3135
log.stdout("Verifing files and paths", level="debug")
3236
self.setup_file_system()
3337
self.language_data = io_toml.load_toml_data(FILE_PATHS.language_data)
@@ -57,11 +61,8 @@ def __init__(self, pref_counter: float) -> None:
5761
provider_urls=self.provider_urls,
5862
language_data=self.language_data,
5963
)
60-
self.refresh_call_conditions_attrs()
61-
log.task_completed()
62-
63-
def refresh_call_conditions_attrs(self) -> None:
6464
self.call_conditions = CallConditions(self)
65+
log.task_completed()
6566

6667
def update_imdb_id(self) -> None:
6768
timeout = self.app_config.request_connect_timeout, self.app_config.request_read_timeout
@@ -93,6 +94,19 @@ def all_providers_disabled(self) -> bool:
9394
return True
9495
return False
9596

97+
def prevent_conflicting_config_settings(self) -> None:
98+
# TODO
99+
# make settings exclusive in GUI
100+
if self.app_config.open_on_no_matches and self.app_config.always_open:
101+
self.app_config.open_on_no_matches = False
102+
io_toml.update_toml_key(FILE_PATHS.config, "download_manager.open_on_no_matches", False)
103+
if (
104+
self.app_config.subtitle_post_processing["move_best"]
105+
and self.app_config.subtitle_post_processing["move_all"]
106+
):
107+
self.app_config.subtitle_post_processing["move_best"] = False
108+
io_toml.update_toml_key(FILE_PATHS.config, "subtitle_post_processing.move_best", False)
109+
96110

97111
class SubsearchCore(Initializer):
98112
def __init__(self, pref_counter: float) -> None:
@@ -102,14 +116,18 @@ def __init__(self, pref_counter: float) -> None:
102116
log.brackets("GUI")
103117
screen_manager.open_screen("search_options")
104118
log.stdout("Exiting GUI", level="debug")
119+
self.prevent_conflicting_config_settings()
105120
return None
106121

107122
if " " in VIDEO_FILE.filename:
108123
log.stdout(f"{VIDEO_FILE.filename} contains spaces, result may vary", level="warning")
109124

110125
if not self.all_providers_disabled():
126+
self.prevent_conflicting_config_settings()
111127
log.brackets("Search started")
112128

129+
130+
113131
@decorators.call_func
114132
def init_search(self, *providers: Callable[..., None]) -> None:
115133
thread_handle._create_threads(*providers)
@@ -146,7 +164,6 @@ def download_files(self) -> None:
146164
else:
147165
s = self.accepted_subtitles.pop(index_position)
148166
self.rejected_subtitles.append(s)
149-
self.refresh_call_conditions_attrs()
150167
log.task_completed()
151168

152169
def _handle_subsource_subtitle(self, index_position: int, subtitle: Subtitle) -> None:
@@ -166,28 +183,33 @@ def download_manager(self) -> None:
166183
subtitles = self.rejected_subtitles + self.accepted_subtitles
167184
screen_manager.open_screen("download_manager", subtitles=subtitles)
168185
self.manually_accepted_subtitles = download_manager.DownloadManager.downloaded_subtitle
186+
self.downloaded_subtitles += len(self.manually_accepted_subtitles)
169187
log.task_completed()
170188

171189
@decorators.call_func
172-
def extract_files(self) -> None:
173-
log.brackets("Extracting downloads")
174-
io_file_system.extract_files_in_dir(VIDEO_FILE.tmp_dir, VIDEO_FILE.subs_dir)
175-
log.task_completed()
176-
177-
@decorators.call_func
178-
def subtitle_post_processing(self):
190+
def subtitle_post_processing(self) -> None:
179191
target = self.app_config.subtitle_post_processing["target_path"]
180192
resolution = self.app_config.subtitle_post_processing["path_resolution"]
181193
target_path = io_file_system.create_path_from_string(target, resolution)
194+
self.downloaded_subtitle_archives = io_file_system.count_files_in_directory(VIDEO_FILE.tmp_dir)
195+
self.extract_files()
196+
self.extracted_subtitle_archives = io_file_system.count_files_in_directory(VIDEO_FILE.subs_dir, [".srt"])
182197
self.subtitle_rename()
183198
self.subtitle_move_best(target_path)
184199
self.subtitle_move_all(target_path)
185200

201+
@decorators.call_func
202+
def extract_files(self) -> None:
203+
log.brackets("Extracting downloads")
204+
io_file_system.extract_files_in_dir(VIDEO_FILE.tmp_dir, VIDEO_FILE.subs_dir)
205+
log.task_completed()
206+
186207
@decorators.call_func
187208
def subtitle_rename(self) -> None:
188209
log.brackets("Renaming best match")
189210
new_name = io_file_system.autoload_rename(VIDEO_FILE.filename, ".srt")
190211
self.autoload_src = new_name
212+
191213
log.task_completed()
192214

193215
@decorators.call_func
@@ -206,14 +228,13 @@ def subtitle_move_all(self, target: Path) -> None:
206228
def summary_notification(self, elapsed) -> None:
207229
log.brackets("Summary toast")
208230
elapsed_summary = f"Finished in {elapsed} seconds"
209-
tot_num_of_subtitles = len(self.accepted_subtitles) + len(self.rejected_subtitles)
210-
all_downloaded = self.downloaded_subtitles + len(self.manually_accepted_subtitles)
211-
matches_downloaded = f"Downloaded: {all_downloaded}/{tot_num_of_subtitles}"
212-
if all_downloaded > 0:
231+
number_of_results = len(self.accepted_subtitles) + len(self.rejected_subtitles)
232+
matches_downloaded = f"Downloaded: {self.downloaded_subtitles}/{number_of_results}"
233+
if self.downloaded_subtitles >= 1:
213234
msg = "Search Succeeded", f"{matches_downloaded}\n{elapsed_summary}"
214235
log.stdout(matches_downloaded, hex_color="#a6e3a1")
215236
self.system_tray.display_toast(*msg)
216-
elif all_downloaded == 0:
237+
elif self.downloaded_subtitles == 0:
217238
msg = "Search Failed", f"{matches_downloaded}\n{elapsed_summary}"
218239
log.stdout(matches_downloaded, hex_color="#f38ba8")
219240
self.system_tray.display_toast(*msg)
@@ -248,14 +269,19 @@ def core_on_exit(self) -> None:
248269
class CallConditions:
249270

250271
def __init__(self, cls: SubsearchCore) -> None:
251-
self.app_config = cls.app_config
252-
self.file_exist = cls.file_exist
253-
self.release_data = cls.release_data
254-
self.provider_urls = cls.provider_urls
255-
self.language_data = cls.language_data
256-
self.accepted_subtitles = cls.accepted_subtitles
257-
self.rejected_subtitles = cls.rejected_subtitles
258-
self.downloaded_subtitles = cls.downloaded_subtitles
272+
self.cls = cls
273+
self.refresh_parent_attrs()
274+
275+
def refresh_parent_attrs(self) -> None:
276+
self.app_config = self.cls.app_config
277+
self.file_exist = self.cls.file_exist
278+
self.release_data = self.cls.release_data
279+
self.provider_urls = self.cls.provider_urls
280+
self.language_data = self.cls.language_data
281+
self.accepted_subtitles = self.cls.accepted_subtitles
282+
self.rejected_subtitles = self.cls.rejected_subtitles
283+
self.downloaded_subtitle_archives = self.cls.downloaded_subtitle_archives
284+
self.extracted_subtitle_archives = self.cls.extracted_subtitle_archives
259285

260286
def check_language_compatibility(self, provider: str) -> bool:
261287
language = self.app_config.current_language
@@ -270,6 +296,7 @@ def all_conditions_true(self, conditions: list[bool]) -> bool:
270296

271297
def call_func(self, *args, **kwargs) -> bool:
272298
func_name = kwargs["func_name"]
299+
self.refresh_parent_attrs()
273300
conditions: dict[str, list[bool]] = {
274301
"init_search": [self.file_exist],
275302
"opensubtitles": [
@@ -292,8 +319,7 @@ def call_func(self, *args, **kwargs) -> bool:
292319
self.app_config.providers["subsource_site"],
293320
],
294321
"download_files": [
295-
len(self.accepted_subtitles) >= 1
296-
and self.app_config.automatic_downloads
322+
(len(self.accepted_subtitles) >= 1 and self.app_config.automatic_downloads)
297323
or (
298324
len(self.accepted_subtitles) >= 1
299325
and not self.app_config.automatic_downloads
@@ -314,27 +340,24 @@ def call_func(self, *args, **kwargs) -> bool:
314340
)
315341
),
316342
],
317-
"extract_files": [
318-
len(self.accepted_subtitles) >= 1,
319-
],
320343
"subtitle_post_processing": [
321344
self.file_exist,
322345
],
346+
"extract_files": [
347+
self.downloaded_subtitle_archives >= 1,
348+
],
323349
"subtitle_rename": [
324-
self.file_exist,
350+
self.extracted_subtitle_archives >= 1,
325351
self.app_config.subtitle_post_processing["rename"],
326-
self.downloaded_subtitles >= 1,
327352
],
328353
"subtitle_move_best": [
329-
self.file_exist,
354+
self.extracted_subtitle_archives >= 1,
330355
self.app_config.subtitle_post_processing["move_best"],
331-
self.downloaded_subtitles >= 1,
332356
not self.app_config.subtitle_post_processing["move_all"],
333357
],
334358
"subtitle_move_all": [
335-
self.file_exist,
359+
self.extracted_subtitle_archives >= 1,
336360
self.app_config.subtitle_post_processing["move_all"],
337-
self.downloaded_subtitles > 1,
338361
],
339362
"summary_notification": [
340363
self.file_exist,

src/subsearch/data/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "2.47.1"
1+
__version__ = "2.47.2"

src/subsearch/globals/_logging.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import threading
44
from datetime import datetime
55
from pathlib import Path
6-
from typing import Callable, Optional
6+
from typing import Any, Callable, Optional
77

88
import picologging as logging
99

@@ -12,8 +12,8 @@
1212
from subsearch.globals.dataclasses import GenericDataClass
1313

1414

15-
def capture_call_info(func) -> Callable[..., None]:
16-
def wrapper(*args, **kwargs):
15+
def capture_call_info(func: Callable[..., Any]) -> Callable[..., Any]:
16+
def wrapper(*args, **kwargs) -> Any:
1717
frame = inspect.currentframe().f_back # type: ignore
1818
current_time = datetime.now().time()
1919
call_time = current_time.strftime("%H:%M:%S.%f")[:-3]

src/subsearch/gui/common_utils.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class WindowPosition(tk.Frame):
99
def __init__(self, parent) -> None:
1010
tk.Frame.__init__(self, parent)
1111

12-
def set(self, **kwargs):
12+
def set(self, **kwargs) -> str:
1313
w = kwargs.get("w", cfg.size.width)
1414
h = kwargs.get("h", cfg.size.height)
1515
ws_value_offset = kwargs.get("ws_value_offset", 0)
@@ -23,7 +23,7 @@ def set(self, **kwargs):
2323

2424

2525
class ToolTip(tk.Toplevel):
26-
def __init__(self, parent, widget, *text_strings, **kwargs):
26+
def __init__(self, parent, widget, *text_strings, **kwargs) -> None:
2727
self.parent = parent
2828
self.widget = widget
2929
self.text = text_strings
@@ -68,7 +68,7 @@ def hide(self) -> None:
6868
self.destroy()
6969

7070

71-
def configure_root(root):
71+
def configure_root(root) -> None:
7272
if io_toml.load_toml_value(FILE_PATHS.config, "gui.context_menu"):
7373
io_winreg.add_context_menu()
7474
root.configure(background=cfg.color.default_bg)

src/subsearch/gui/screen_manager.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def release_menu_btn(self, event) -> None:
7575
self.activate_screen()
7676
self.deactivate_screen()
7777

78-
def get_btn(self, btns: dict, event_: Any, equals=True):
78+
def get_btn(self, btns: dict, event_: Any, equals=True) -> tuple | None:
7979
for btn_key, btn_widget in btns.items():
8080
if event_.widget == btn_widget and equals:
8181
return btn_key, btn_widget
@@ -145,6 +145,6 @@ def open_screen(tab_name: str, **kwargs) -> None:
145145
root.mainloop()
146146

147147

148-
def close_mainloop(event):
148+
def close_mainloop(event) -> None:
149149
if event.keysym == "Escape":
150150
root.destroy()

src/subsearch/gui/screens/search_options.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -204,14 +204,14 @@ def __init__(self, parent) -> None:
204204
apply_input.bind("<Leave>", self.leave_btn_apply_input)
205205
self.entry_path.bind("<Enter>", self.enter_entry_path)
206206

207-
def enter_entry_path(self, event):
207+
def enter_entry_path(self, event) -> None:
208208
widget_ = event.widget
209209
path = widget_.get()
210210
old_path = path.replace("Invalid path: ", "")
211211
widget_.delete(0, tk.END)
212212
widget_.insert(tk.END, old_path)
213213

214-
def enter_btn_apply_input(self, event):
214+
def enter_btn_apply_input(self, event) -> None:
215215
widget_ = event.widget
216216
path = self.verify_path()
217217
if self.entry_path.instate(["invalid"]):
@@ -224,7 +224,7 @@ def enter_btn_resolution(self, event) -> None:
224224
widget_ = event.widget
225225
widget_.bind("<ButtonPress-1>", self.toogle_path_resolution)
226226

227-
def leave_btn_apply_input(self, event):
227+
def leave_btn_apply_input(self, event) -> None:
228228
widget_ = event.widget
229229
path = self.verify_path()
230230
if self.entry_path.instate(["invalid"]):
@@ -237,7 +237,7 @@ def leave_btn_resolution(self, event) -> None:
237237
widget_ = event.widget
238238
widget_.unbind("<ButtonPress-1>")
239239

240-
def update_config(self, event):
240+
def update_config(self, event) -> None:
241241
target_path = self.entry_path.get()
242242
path_resolution = self.path_resolution.get().lower()
243243
io_toml.update_toml_key(FILE_PATHS.config, "subtitle_post_processing.target_path", target_path)
@@ -252,7 +252,7 @@ def verify_path(self) -> str:
252252
self.entry_path.state(["!invalid"])
253253
return target_path
254254

255-
def on_invalid_state(self, path):
255+
def on_invalid_state(self, path) -> None:
256256
invalid_path = path.replace("Invalid path: ", "")
257257
self.entry_path.delete(0, tk.END)
258258
self.entry_path.insert(tk.END, f"Invalid path: {invalid_path}")
@@ -310,7 +310,7 @@ def __init__(self, parent) -> None:
310310
self.slider.bind("<Enter>", self.enter_button)
311311
self.slider.bind("<Leave>", self.leave_button)
312312

313-
def get_value(self):
313+
def get_value(self) -> int:
314314
return self.current_value.get()
315315

316316
def set_value(self, event) -> None:

0 commit comments

Comments
 (0)