Reduce startup time
This commit is contained in:
parent
526069dabf
commit
31cf687e2f
@ -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"]
|
||||
|
@ -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:
|
||||
|
@ -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]:
|
||||
|
@ -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))
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user