Reduce startup time

This commit is contained in:
Timmy Welch 2023-06-22 20:11:40 -07:00
parent 526069dabf
commit 31cf687e2f
8 changed files with 81 additions and 68 deletions

View File

@ -2,8 +2,6 @@ from __future__ import annotations
from comicapi.archivers.archiver import Archiver
from comicapi.archivers.folder import FolderArchiver
from comicapi.archivers.rar import RarArchiver
from comicapi.archivers.sevenzip import SevenZipArchiver
from comicapi.archivers.zip import ZipArchiver
@ -12,4 +10,4 @@ class UnknownArchiver(Archiver):
return "Unknown"
__all__ = ["Archiver", "UnknownArchiver", "FolderArchiver", "RarArchiver", "ZipArchiver", "SevenZipArchiver"]
__all__ = ["Archiver", "UnknownArchiver", "FolderArchiver", "ZipArchiver"]

View File

@ -22,8 +22,6 @@ import shutil
import sys
from typing import cast
import wordninja
from comicapi import filenamelexer, filenameparser, utils
from comicapi.archivers import Archiver, UnknownArchiver, ZipArchiver
from comicapi.comet import CoMet
@ -31,28 +29,17 @@ from comicapi.comicbookinfo import ComicBookInfo
from comicapi.comicinfoxml import ComicInfoXml
from comicapi.genericmetadata import GenericMetadata, PageType
if sys.version_info < (3, 10):
from importlib_metadata import entry_points
else:
from importlib.metadata import entry_points
try:
from PIL import Image
pil_available = True
except ImportError:
pil_available = False
logger = logging.getLogger(__name__)
if not pil_available:
logger.error("PIL unavalable")
archivers: list[type[Archiver]] = []
def load_archive_plugins() -> None:
if not archivers:
if sys.version_info < (3, 10):
from importlib_metadata import entry_points
else:
from importlib.metadata import entry_points
builtin: list[type[Archiver]] = []
for arch in entry_points(group="comicapi.archiver"):
try:
@ -77,6 +64,7 @@ class MetaDataStyle:
class ComicArchive:
logo_data = b""
pil_available = True
def __init__(self, path: pathlib.Path | str, default_image_path: pathlib.Path | str | None = None) -> None:
self.cbi_md: GenericMetadata | None = None
@ -517,7 +505,13 @@ class ComicArchive:
if calc_page_sizes:
for index, p in enumerate(md.pages):
idx = int(p["Image"])
if pil_available:
if self.pil_available:
try:
from PIL import Image
self.pil_available = True
except ImportError:
self.pil_available = False
if "ImageSize" not in p or "ImageHeight" not in p or "ImageWidth" not in p:
data = self.get_page(idx)
if data:
@ -552,6 +546,8 @@ class ComicArchive:
filename = self.path.name
if split_words:
import wordninja
filename = " ".join(wordninja.split(self.path.stem)) + self.path.suffix
if complicated_parser:

View File

@ -25,10 +25,6 @@ from collections.abc import Iterable, Mapping
from shutil import which # noqa: F401
from typing import Any
import natsort
import pycountry
import rapidfuzz.fuzz
import comicapi.data
try:
@ -43,6 +39,8 @@ logger = logging.getLogger(__name__)
def _custom_key(tup):
import natsort
lst = []
for x in natsort.os_sort_keygen()(tup):
ret = x
@ -54,6 +52,8 @@ def _custom_key(tup):
def os_sorted(lst: Iterable) -> Iterable:
import natsort
key = _custom_key
if icu_available or platform.system() == "Windows":
key = natsort.os_sort_keygen()
@ -198,6 +198,8 @@ def sanitize_title(text: str, basic: bool = False) -> str:
def titles_match(search_title: str, record_title: str, threshold: int = 90) -> bool:
import rapidfuzz.fuzz
sanitized_search = sanitize_title(search_title)
sanitized_record = sanitize_title(record_title)
ratio = int(rapidfuzz.fuzz.ratio(sanitized_search, sanitized_record))
@ -221,26 +223,40 @@ def unique_file(file_name: pathlib.Path) -> pathlib.Path:
counter += 1
languages: dict[str | None, str | None] = defaultdict(lambda: None)
_languages: dict[str | None, str | None] = defaultdict(lambda: None)
countries: dict[str | None, str | None] = defaultdict(lambda: None)
_countries: dict[str | None, str | None] = defaultdict(lambda: None)
for c in pycountry.countries:
if "alpha_2" in c._fields:
countries[c.alpha_2] = c.name
for lng in pycountry.languages:
if "alpha_2" in lng._fields:
languages[lng.alpha_2] = lng.name
def countries() -> dict[str | None, str | None]:
if not _countries:
import pycountry
for c in pycountry.countries:
if "alpha_2" in c._fields:
_countries[c.alpha_2] = c.name
return _countries
def languages() -> dict[str | None, str | None]:
if not _languages:
import pycountry
for lng in pycountry.languages:
if "alpha_2" in lng._fields:
_languages[lng.alpha_2] = lng.name
return _languages
def get_language_from_iso(iso: str | None) -> str | None:
return languages[iso]
return languages()[iso]
def get_language_iso(string: str | None) -> str | None:
if string is None:
return None
import pycountry
# Return current string if all else fails
lang = string.casefold()
@ -252,7 +268,7 @@ def get_language_iso(string: str | None) -> str | None:
def get_country_from_iso(iso: str | None) -> str | None:
return countries[iso]
return countries()[iso]
def get_publisher(publisher: str) -> tuple[str, str]:

View File

@ -22,18 +22,15 @@ import pathlib
import shutil
import sqlite3 as lite
import tempfile
from typing import TYPE_CHECKING
import requests
from comictaggerlib import ctversion
try:
if TYPE_CHECKING:
from PyQt5 import QtCore, QtNetwork
qt_available = True
except ImportError:
qt_available = False
logger = logging.getLogger(__name__)
@ -47,6 +44,7 @@ def fetch_complete(url: str, image_data: bytes | QtCore.QByteArray) -> None:
class ImageFetcher:
image_fetch_complete = fetch_complete
qt_available = True
def __init__(self, cache_folder: pathlib.Path) -> None:
self.db_file = cache_folder / "image_url_cache.db"
@ -55,10 +53,17 @@ class ImageFetcher:
self.user_data = None
self.fetched_url = ""
if self.qt_available:
try:
from PyQt5 import QtNetwork
self.qt_available = True
except ImportError:
self.qt_available = False
if not os.path.exists(self.db_file):
self.create_image_db()
if qt_available:
if self.qt_available:
self.nam = QtNetwork.QNetworkAccessManager()
def clear_cache(self) -> None:
@ -79,7 +84,7 @@ class ImageFetcher:
# first look in the DB
image_data = self.get_image_from_cache(url)
# Async for retrieving covers seems to work well
if blocking or not qt_available:
if blocking or not self.qt_available:
if not image_data:
try:
image_data = requests.get(url, headers={"user-agent": "comictagger/" + ctversion.version}).content
@ -91,7 +96,9 @@ class ImageFetcher:
ImageFetcher.image_fetch_complete(url, image_data)
return image_data
if qt_available:
if self.qt_available:
from PyQt5 import QtCore, QtNetwork
# if we found it, just emit the signal asap
if image_data:
ImageFetcher.image_fetch_complete(url, QtCore.QByteArray(image_data))

View File

@ -43,14 +43,6 @@ else:
logger = logging.getLogger("comictagger")
try:
from comictaggerlib import gui
qt_available = gui.qt_available
except Exception:
logger.exception("Qt unavailable")
qt_available = False
logger.setLevel(logging.DEBUG)
@ -190,10 +182,6 @@ class App:
comicapi.utils.load_publishers()
update_publishers(self.config)
if not qt_available and not self.config[0].runtime_no_gui:
self.config[0].runtime_no_gui = True
logger.warning("PyQt5 is not available. ComicTagger is limited to command-line mode.")
# manage the CV API key
# None comparison is used so that the empty string can unset the value
if not error and (
@ -215,16 +203,23 @@ class App:
True,
)
if self.config[0].runtime_no_gui:
if error and error[1]:
print(f"A fatal error occurred please check the log for more information: {error[0]}") # noqa: T201
raise SystemExit(1)
if not self.config[0].runtime_no_gui:
try:
cli.CLI(self.config[0], talkers).run()
except Exception:
logger.exception("CLI mode failed")
else:
gui.open_tagger_window(talkers, self.config, error)
from comictaggerlib import gui
return gui.open_tagger_window(talkers, self.config, error)
except ImportError:
self.config[0].runtime_no_gui = True
logger.warning("PyQt5 is not available. ComicTagger is limited to command-line mode.")
# GUI mode is not available or CLI mode was requested
if error and error[1]:
print(f"A fatal error occurred please check the log for more information: {error[0]}") # noqa: T201
raise SystemExit(1)
try:
cli.CLI(self.config[0], talkers).run()
except Exception:
logger.exception("CLI mode failed")
def main() -> None:

View File

@ -1398,13 +1398,13 @@ class TaggerWindow(QtWidgets.QMainWindow):
# Add the entries to the country combobox
self.cbCountry.addItem("", "")
for f in natsort.humansorted(utils.countries.items(), operator.itemgetter(1)):
for f in natsort.humansorted(utils.countries().items(), operator.itemgetter(1)):
self.cbCountry.addItem(f[1], f[0])
# Add the entries to the language combobox
self.cbLanguage.addItem("", "")
for f in natsort.humansorted(utils.languages.items(), operator.itemgetter(1)):
for f in natsort.humansorted(utils.languages().items(), operator.itemgetter(1)):
self.cbLanguage.addItem(f[1], f[0])
# Add the entries to the manga combobox

View File

@ -18,8 +18,6 @@ import posixpath
import re
from urllib.parse import urlsplit
from bs4 import BeautifulSoup
from comicapi import utils
from comicapi.genericmetadata import GenericMetadata
from comicapi.issuestring import IssueString
@ -132,6 +130,8 @@ def cleanup_html(string: str, remove_html_tables: bool = False) -> str:
"""Cleans HTML code from any text. Will remove any HTML tables with remove_html_tables"""
if string is None:
return ""
from bs4 import BeautifulSoup
# find any tables
soup = BeautifulSoup(string, "html.parser")
tables = soup.findAll("table")

View File

@ -6,6 +6,7 @@ import shutil
import pytest
from importlib_metadata import entry_points
import comicapi.archivers.rar
import comicapi.comicarchive
import comicapi.genericmetadata
from testing.filenames import datadir