Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement subsource as a provider #697

Merged
merged 40 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
2ee2be0
Feat add subsource as a provider
vagabondHustler Jul 15, 2024
a5a2829
Feat add subsource to `ProviderUrls`
vagabondHustler Jul 15, 2024
16cde37
Feat add subsource `call_func` logic
vagabondHustler Jul 15, 2024
8807478
Feat add subsource to `provider_options`
vagabondHustler Jul 15, 2024
e89c1c9
Refactor add type hints
vagabondHustler Jul 15, 2024
b49bbf1
Feat add subsource to list supported providers
vagabondHustler Jul 15, 2024
0085446
Feat add subsource
vagabondHustler Jul 15, 2024
675e351
Refactor add imdb_id to `ReleaseData`
vagabondHustler Jul 16, 2024
fdc2dea
Refactor add new data to `ProviderDataContainer`
vagabondHustler Jul 16, 2024
a80b38a
Feat add way to search for tvseries with subsource
vagabondHustler Jul 16, 2024
bc33f00
Refactor update when imdb_lookup is used
vagabondHustler Jul 16, 2024
25daedf
Feat add `remove_padded_zero` function to module
vagabondHustler Jul 16, 2024
916cf11
Chore added todo
vagabondHustler Jul 16, 2024
04d3374
Refactor subsoruce
vagabondHustler Jul 17, 2024
7fe0364
Refactor add new dataclasses
vagabondHustler Jul 17, 2024
ad85003
Refactor providers and provider utils
vagabondHustler Jul 17, 2024
097753a
Refactor update `app_config` variable names
vagabondHustler Jul 17, 2024
0404c03
Fix bug in tests
vagabondHustler Jul 17, 2024
7b44606
Updated todo
vagabondHustler Jul 17, 2024
5c88092
Refactor everything
vagabondHustler Jul 21, 2024
22c0c1b
Feat add subsource functionality to download_manger
vagabondHustler Jul 21, 2024
33a9aa9
Reformat with black
vagabondHustler Jul 21, 2024
3c46174
Fix downloaded message in summary toast
vagabondHustler Jul 21, 2024
cefb8f3
Feat add subsource as a provider
vagabondHustler Jul 15, 2024
22e0933
Fix bug in tests
vagabondHustler Jul 17, 2024
21599d0
Fix downloaded message in summary toast
vagabondHustler Jul 21, 2024
718d265
Merge branch 'feat/subsource/133320' of https://github.com/vagabondHu…
vagabondHustler Jul 21, 2024
8d27678
Fix unmatched f-string
vagabondHustler Jul 21, 2024
c119db6
Merge branch 'dev' into feat/subsource/133320
vagabondHustler Jul 21, 2024
abb2fcc
Fix add _handle_subsource_suibtitle if statement
vagabondHustler Jul 22, 2024
01a7888
Fix incorrect value being displayed during download
vagabondHustler Jul 22, 2024
58a8603
Merge branch 'feat/subsource/133320' of https://github.com/vagabondHu…
vagabondHustler Jul 22, 2024
a1a4a73
Fix infinite loop if a provider can't be reached
vagabondHustler Jul 22, 2024
97bd504
Fix infinite loop if a provider can't be reached (#700)
vagabondHustler Jul 22, 2024
7715e46
Refactor tweak api_call_limit default value
vagabondHustler Jul 22, 2024
f7c9c74
Merge branch 'dev' into feat/subsource/133320
vagabondHustler Jul 22, 2024
2b26bff
Merge branch 'feat/subsource/133320' of https://github.com/vagabondHu…
vagabondHustler Jul 22, 2024
0a08772
Refactor del api_sleep_ms
vagabondHustler Jul 22, 2024
02976ee
Refactor update default values in `advanced_user`
vagabondHustler Jul 22, 2024
90775d0
Merge branch 'dev' into feat/subsource/133320
vagabondHustler Jul 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 8 additions & 17 deletions src/subsearch/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,27 @@ class Subsearch:
def __init__(self) -> None:
self.subsearch_core = core.SubsearchCore(PREF_COUNTER)

def search_for_subtitles(self) -> None:
def start_app(self) -> None:
self.subsearch_core.init_search(
self.provider_opensubtitles,
self.provider_yifysubtitles,
self.subsearch_core.opensubtitles,
self.subsearch_core.yifysubtitles,
self.subsearch_core.subsource,
)

def provider_opensubtitles(self) -> None:
self.subsearch_core.opensubtitles()


def provider_yifysubtitles(self) -> None:
self.subsearch_core.yifysubtitles()

def process_files(self) -> None:
self.subsearch_core.download_files()
self.subsearch_core.download_manager()
self.subsearch_core.extract_files()
self.subsearch_core.subtitle_post_processing()
self.subsearch_core.clean_up()

def on_exit(self) -> None:
def exit_app(self) -> None:
self.subsearch_core.core_on_exit()


@decorators.apply_mutex
def main() -> None:
app = Subsearch()
app.search_for_subtitles()
app.process_files()
app.on_exit()
subsearch = Subsearch()
subsearch.start_app()
subsearch.exit_app()


if __name__ == "__main__":
Expand Down
211 changes: 170 additions & 41 deletions src/subsearch/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,34 @@
from subsearch.globals.dataclasses import Subtitle
from subsearch.gui import screen_manager, system_tray
from subsearch.gui.screens import download_manager
from subsearch.providers import opensubtitles, yifysubtitles
from subsearch.utils import io_file_system, io_toml, string_parser
from subsearch.providers import opensubtitles, subsource, yifysubtitles
from subsearch.utils import imdb_lookup, io_file_system, io_toml, string_parser


class Initializer:
def __init__(self, pref_counter: float) -> None:
log.brackets("Initializing")
log.stdout(f"Loading components...", level="info", end_new_line=True)
self.file_exist = True if VIDEO_FILE else False
self.setup_file_system()
self.start = pref_counter
log.brackets("Initializing")

self.api_calls_made: dict[str, int] = {}
self.downloaded_subtitles = 0
self.ran_download_tab = False
self.accepted_subtitles: list[Subtitle] = []
self.rejected_subtitles: list[Subtitle] = []
self.manually_accepted_subtitles: list[Subtitle] = []
self.release_data = string_parser.no_release_data()
self.provider_urls = string_parser.CreateProviderUrls.no_urls()
self.file_exist = VIDEO_FILE.file_exist

log.stdout("Verifing files and paths", level="debug")
self.setup_file_system()
self.language_data = io_toml.load_toml_data(FILE_PATHS.language_data)
self.app_config = io_toml.get_app_config(FILE_PATHS.config)

log.dataclass(DEVICE_INFO, level="debug", print_allowed=False)
log.dataclass(self.app_config, level="debug", print_allowed=False)

log.stdout("Initializing system tray icon", level="debug")
decorators.enable_system_tray = self.app_config.system_tray
self.system_tray = system_tray.SystemTray()
self.system_tray.start()
Expand All @@ -31,16 +44,8 @@ def __init__(self, pref_counter: float) -> None:
VIDEO_FILE.file_hash = io_file_system.get_file_hash(VIDEO_FILE.file_path)
log.dataclass(VIDEO_FILE, level="debug", print_allowed=False)
io_file_system.create_directory(VIDEO_FILE.file_directory)

self.downloaded_subtitles = 0
self.ran_download_tab = False
self.accepted_subtitles: list[Subtitle] = []
self.rejected_subtitles: list[Subtitle] = []
self.manually_accepted_subtitles: list[Subtitle] = []
self.language_data = io_toml.load_toml_data(FILE_PATHS.language_data)

if self.file_exist:
self.release_data = string_parser.get_release_data(VIDEO_FILE.filename)
self.update_imdb_id()
log.dataclass(self.release_data, level="debug", print_allowed=False)
provider_urls = string_parser.CreateProviderUrls(self.app_config, self.release_data, self.language_data)
self.provider_urls = provider_urls.retrieve_urls()
Expand All @@ -51,10 +56,22 @@ def __init__(self, pref_counter: float) -> None:
provider_urls=self.provider_urls,
language_data=self.language_data,
)

self.call_conditions = CallConditions(self)
log.task_completed()

def update_imdb_id(self) -> None:
timeout = self.app_config.request_connect_timeout, self.app_config.request_read_timeout
find_id = imdb_lookup.FindImdbID(
self.release_data.title,
self.release_data.year,
self.release_data.tvseries,
request_timeout=timeout,
)
self.release_data.imdb_id = find_id.imdb_id

def setup_file_system(self) -> None:
log.stdout("Verifing files and paths", level="debug")

io_file_system.create_directory(APP_PATHS.tmp_dir)
io_file_system.create_directory(APP_PATHS.appdata_subsearch)
io_toml.resolve_on_integrity_failure()
Expand All @@ -68,6 +85,7 @@ def all_providers_disabled(self) -> bool:
self.app_config.providers["opensubtitles_site"] is False
and self.app_config.providers["opensubtitles_hash"] is False
and self.app_config.providers["yifysubtitles_site"] is False
and self.app_config.providers["subsource_site"] is False
):
return True
return False
Expand All @@ -91,47 +109,59 @@ def __init__(self, pref_counter: float) -> None:

@decorators.call_func
def init_search(self, *providers: Callable[..., None]) -> None:
self._create_threads(*providers)
thread_handle._create_threads(*providers)
log.task_completed()

def _create_threads(self, *tasks) -> None:
for thread_count, target in enumerate(tasks, start=1):
func_name = str(target.__name__).split("_")[-1]
name = f"thread_{thread_count}_{func_name}"
task_thread = thread_handle.CreateThread(target=target, name=name)
task_thread.start()
task_thread.join()

def _start_search(self, provider, flag: str) -> None:
search_provider = provider(**self.search_kwargs)
search_provider.start_search(flag=flag)
self.accepted_subtitles.extend(search_provider.accepted_subtitles)
self.rejected_subtitles.extend(search_provider.rejected_subtitles)

@decorators.call_func
def opensubtitles(self) -> None:
self._start_search(provider=opensubtitles.OpenSubtitles, flag="hash")
self._start_search(provider=opensubtitles.OpenSubtitles, flag="site")
thread_handle._start_search(self, provider=opensubtitles.OpenSubtitles, flag="hash")
thread_handle._start_search(self, provider=opensubtitles.OpenSubtitles, flag="site")

@decorators.call_func
def yifysubtitles(self) -> None:
self._start_search(provider=yifysubtitles.YifiSubtitles, flag="site")
thread_handle._start_search(self, provider=yifysubtitles.YifiSubtitles, flag="site")

@decorators.call_func
def subsource(self) -> None:
thread_handle._start_search(self, provider=subsource.Subsource, flag="site")

@decorators.call_func
def download_files(self) -> None:
log.brackets(f"Downloading subtitles")
index_size = len(self.accepted_subtitles)
for enum, subtitle in enumerate(self.accepted_subtitles, 1):
io_file_system.download_subtitle(subtitle, enum, index_size)
self.downloaded_subtitles += 1
self.accepted_subtitles = sorted(self.accepted_subtitles, key=lambda i: i.precentage_result, reverse=True)
for index_position, subtitle in enumerate(self.accepted_subtitles):
if subtitle.provider_name not in self.api_calls_made:
self.api_calls_made[subtitle.provider_name] = 0
if not self.api_calls_made[subtitle.provider_name] == self.app_config.api_call_limit:
if subtitle.provider_name == "subsource":
self._handle_subsource_subtitle(index_position, subtitle)
sub_count = sum(self.api_calls_made.values(), 1)
io_file_system.download_subtitle(subtitle, sub_count, index_size)
self.downloaded_subtitles += 1
self.api_calls_made[subtitle.provider_name] += 1
else:
s = self.accepted_subtitles.pop(index_position)
self.rejected_subtitles.append(s)
log.task_completed()

def _handle_subsource_subtitle(self, index_position: int, subtitle: Subtitle) -> None:
if self.app_config.providers["subsource_site"]:
subsource_api = subsource.GetDownloadUrl()
download_url = subsource_api.get_url(subtitle)
if not download_url:
self.accepted_subtitles.pop(index_position)
log.stdout(f"{subtitle.provider_name} could not be reached. Removed {subtitle.subtitle_name}")
else:
self.accepted_subtitles[index_position].download_url = download_url
self.accepted_subtitles[index_position].request_data = {}

@decorators.call_func
def download_manager(self) -> None:
log.brackets(f"Download Manager")
subtitles = self.rejected_subtitles + self.accepted_subtitles
screen_manager.open_screen("download_manager", subtitles=subtitles)
self.manually_accepted_subtitles.extend(download_manager.DownloadManager.downloaded_subtitle)
self.manually_accepted_subtitles = download_manager.DownloadManager.downloaded_subtitle
log.task_completed()

@decorators.call_func
Expand Down Expand Up @@ -173,12 +203,13 @@ def summary_notification(self, elapsed) -> None:
log.brackets("Summary toast")
elapsed_summary = f"Finished in {elapsed} seconds"
tot_num_of_subtitles = len(self.accepted_subtitles) + len(self.rejected_subtitles)
matches_downloaded = f"Downloaded: {self.downloaded_subtitles}/{tot_num_of_subtitles}"
if self.downloaded_subtitles > 0:
all_downloaded = self.downloaded_subtitles + len(self.manually_accepted_subtitles)
matches_downloaded = f"Downloaded: {all_downloaded}/{tot_num_of_subtitles}"
if all_downloaded > 0:
msg = "Search Succeeded", f"{matches_downloaded}\n{elapsed_summary}"
log.stdout(matches_downloaded, hex_color="#a6e3a1")
self.system_tray.display_toast(*msg)
elif self.downloaded_subtitles == 0:
elif all_downloaded == 0:
msg = "Search Failed", f"{matches_downloaded}\n{elapsed_summary}"
log.stdout(matches_downloaded, hex_color="#f38ba8")
self.system_tray.display_toast(*msg)
Expand Down Expand Up @@ -208,3 +239,101 @@ def core_on_exit(self) -> None:
input("Enter to exit")
except KeyboardInterrupt:
pass


class CallConditions:

def __init__(self, cls: Initializer) -> None:
self.app_config = cls.app_config
self.file_exist = cls.file_exist
self.release_data = cls.release_data
self.provider_urls = cls.provider_urls
self.language_data = cls.language_data
self.accepted_subtitles = cls.accepted_subtitles
self.rejected_subtitles = cls.rejected_subtitles
self.downloaded_subtitles = cls.downloaded_subtitles

def check_language_compatibility(self, provider: str) -> bool:
language = self.app_config.current_language
if provider in self.language_data[language]:
return False
return True

def all_conditions_true(self, conditions: list[bool]) -> bool:
if False in conditions:
return False
return True

def call_func(self, *args, **kwargs) -> bool:
func_name = kwargs["func_name"]
conditions: dict[str, list[bool]] = {
"init_search": [self.file_exist],
"opensubtitles": [
self.file_exist,
lambda: self.check_language_compatibility("opensubtitles"),
self.app_config.providers["opensubtitles_hash"] or self.app_config.providers["opensubtitles_site"],
],
"yifysubtitles": [
self.file_exist,
not self.app_config.only_foreign_parts,
lambda: self.check_language_compatibility("yifysubtitles"),
not self.release_data.tvseries,
not self.provider_urls.yifysubtitles == "",
self.app_config.providers["yifysubtitles_site"],
],
"subsource": [
self.file_exist,
not self.app_config.only_foreign_parts,
lambda: self.check_language_compatibility("subsource"),
self.app_config.providers["subsource_site"],
],
"download_files": [
len(self.accepted_subtitles) >= 1
and self.app_config.automatic_downloads
or (
len(self.accepted_subtitles) >= 1
and not self.app_config.automatic_downloads
and not self.app_config.always_open
and not self.app_config.open_on_no_matches
),
],
"download_manager": [
(
(
len(self.accepted_subtitles) == 0
and len(self.rejected_subtitles) >= 1
and self.app_config.open_on_no_matches
)
or (
(len(self.accepted_subtitles) >= 1 or len(self.rejected_subtitles) >= 1)
and self.app_config.always_open
)
),
],
"extract_files": [
len(self.accepted_subtitles) >= 1,
],
"subtitle_post_processing": [
self.file_exist,
],
"subtitle_rename": [
self.app_config.subtitle_post_processing["rename"],
self.downloaded_subtitles >= 1,
],
"subtitle_move_best": [
self.app_config.subtitle_post_processing["move_best"],
self.downloaded_subtitles >= 1,
not self.app_config.subtitle_post_processing["move_all"],
],
"subtitle_move_all": [
self.app_config.subtitle_post_processing["move_all"],
self.downloaded_subtitles > 1,
],
"summary_notification": [
self.file_exist,
self.app_config.summary_notification,
],
"clean_up": [self.file_exist],
}

return self.all_conditions_true(conditions[func_name])
2 changes: 1 addition & 1 deletion src/subsearch/globals/_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,4 @@ def dataclass(self, instance: GenericDataClass, **kwargs) -> None:

@capture_call_info
def task_completed(self, **kwargs) -> None:
self("Task completed", level="info", hex_color="#89b4fa", **kwargs)
self("Tasks completed", level="info", hex_color="#89b4fa", **kwargs)
2 changes: 1 addition & 1 deletion src/subsearch/globals/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
REGISTRY_PATHS = io_app.get_windows_registry_paths()
COMPUTER_NAME = io_app.get_computer_name()
VERSION = io_app.get_app_version()
GUID = io_app.get_guid()
GUID = io_app.get_guid()
Loading