Merge branch 'develop' into infosources

# Conflicts:
#	comictaggerlib/comicvinetalker.py
#	comictaggerlib/coverimagewidget.py
#	comictaggerlib/main.py
#	comictaggerlib/pagebrowser.py
#	comictaggerlib/pagelisteditor.py
#	comictaggerlib/settings.py
#	comictaggerlib/settingswindow.py
This commit is contained in:
Mizaki 2022-10-30 01:31:58 +01:00
commit 8cac2c255f
40 changed files with 430 additions and 133 deletions

View File

@ -74,6 +74,7 @@ jobs:
- name: Install build dependencies
run: |
pip install --force-reinstall git+https://github.com/pyinstaller/pyinstaller
python -m pip install --upgrade --upgrade-strategy eager -r requirements_dev.txt
- name: Install Windows build dependencies

View File

@ -35,6 +35,7 @@ jobs:
- name: Install build dependencies
run: |
pip install --force-reinstall git+https://github.com/pyinstaller/pyinstaller
python -m pip install --upgrade --upgrade-strategy eager -r requirements_dev.txt
- name: Install Windows build dependencies
@ -78,13 +79,14 @@ jobs:
if: startsWith(github.ref, 'refs/tags/')
shell: bash
run: |
git fetch --depth=1 origin +refs/tags/*:refs/tags/* # github is dumb
echo "release_name=$(git tag -l --format "%(refname:strip=2): %(contents:lines=1)" ${{ github.ref_name }})" >> $GITHUB_ENV
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
name: env.release_name
name: "${{ env.release_name }}"
prerelease: "${{ contains(github.ref, '-') }}" # alpha-releases should be 1.3.0-alpha.x full releases should be 1.3.0
draft: false
files: |

View File

@ -10,7 +10,7 @@ repos:
- id: name-tests-test
- id: requirements-txt-fixer
- repo: https://github.com/asottile/setup-cfg-fmt
rev: v2.0.0
rev: v2.2.0
hooks:
- id: setup-cfg-fmt
- repo: https://github.com/PyCQA/isort
@ -19,16 +19,16 @@ repos:
- id: isort
args: [--af,--add-import, 'from __future__ import annotations']
- repo: https://github.com/asottile/pyupgrade
rev: v2.37.3
rev: v3.1.0
hooks:
- id: pyupgrade
args: [--py39-plus]
- repo: https://github.com/psf/black
rev: 22.6.0
rev: 22.10.0
hooks:
- id: black
- repo: https://github.com/PyCQA/autoflake
rev: v1.4
rev: v1.7.7
hooks:
- id: autoflake
args: [-i]
@ -38,7 +38,7 @@ repos:
- id: flake8
additional_dependencies: [flake8-encodings, flake8-warnings, flake8-builtins, flake8-length, flake8-print]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.971
rev: v0.982
hooks:
- id: mypy
additional_dependencies: [types-setuptools, types-requests]

View File

@ -0,0 +1,7 @@
from __future__ import annotations
import os
def get_hook_dirs() -> list[str]:
return [os.path.dirname(__file__)]

View File

@ -0,0 +1,6 @@
from __future__ import annotations
from PyInstaller.utils.hooks import collect_data_files
datas = []
datas += collect_data_files("comicapi.data")

View File

@ -0,0 +1,5 @@
from __future__ import annotations
import pathlib
data_path = pathlib.Path(__file__).parent

View File

@ -26,7 +26,9 @@ from shutil import which # noqa: F401
from typing import Any
import pycountry
import thefuzz.fuzz
import rapidfuzz.fuzz
import comicapi.data
logger = logging.getLogger(__name__)
@ -155,7 +157,7 @@ def sanitize_title(text: str, basic: bool = False) -> str:
def titles_match(search_title: str, record_title: str, threshold: int = 90) -> bool:
sanitized_search = sanitize_title(search_title)
sanitized_record = sanitize_title(record_title)
ratio: int = thefuzz.fuzz.ratio(sanitized_search, sanitized_record)
ratio: int = rapidfuzz.fuzz.ratio(sanitized_search, sanitized_record)
logger.debug(
"search title: %s ; record title: %s ; ratio: %d ; match threshold: %d",
search_title,
@ -257,6 +259,6 @@ publishers: dict[str, ImprintDict] = {}
def load_publishers() -> None:
try:
update_publishers(json.loads((pathlib.Path(__file__).parent / "data" / "publishers.json").read_text("utf-8")))
update_publishers(json.loads((comicapi.data.data_path / "publishers.json").read_text("utf-8")))
except Exception:
logger.exception("Failed to load publishers.json; The are no publishers or imprints loaded")

View File

@ -1,51 +1,265 @@
# -*- mode: python -*-
# -*- mode: python ; coding: utf-8 -*-
import platform
from os.path import join
from comictaggerlib import ctversion
from PyInstaller.utils.hooks import get_module_file_attribute
enable_console = False
binaries = []
block_cipher = None
if platform.system() == "Windows":
a = Analysis(
["comictagger.py"],
pathex=[],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
exe_binaries = []
exe_zipfiles = []
exe_datas = []
exe_exclude_binaries = True
coll_binaries = a.binaries
coll_zipfiles = a.zipfiles
coll_datas = a.datas
if platform.system() in ["Windows"]:
enable_console = True
exe_binaries = a.binaries
exe_zipfiles = a.zipfiles
exe_datas = a.datas
exe_exclude_binaries = False
a = Analysis(['comictagger.py'],
binaries=binaries,
datas=[('comictaggerlib/ui/*.ui', 'ui'), ('comictaggerlib/graphics', 'graphics'), ('comicapi/data', 'comicapi/data'),(os.path.join(os.path.dirname(get_module_file_attribute('wordninja')),"wordninja"), "wordninja")],
hiddenimports=['PIL'],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
# single file setup
exclude_binaries=False,
name='comictagger',
debug=False,
strip=False,
upx=True,
console=enable_console,
icon="windows/app.ico" )
coll_binaries = []
coll_zipfiles = []
coll_datas = []
app = BUNDLE(exe,
name='ComicTagger.app',
icon='mac/app.icns',
info_plist={
'NSHighResolutionCapable': 'True',
'NSRequiresAquaSystemAppearance': 'False',
'CFBundleDisplayName': 'ComicTagger',
'CFBundleShortVersionString': ctversion.version,
'CFBundleVersion': ctversion.version
},
bundle_identifier=None)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
exe_binaries,
exe_zipfiles,
exe_datas,
[],
exclude_binaries=exe_exclude_binaries,
name="comictagger",
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=enable_console,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon="windows/app.ico",
)
if platform.system() not in ["Windows"]:
coll = COLLECT(
exe,
coll_binaries,
coll_zipfiles,
coll_datas,
strip=False,
upx=True,
upx_exclude=[],
name="comictagger",
)
app = BUNDLE(
coll,
name="ComicTagger.app",
icon="mac/app.icns",
info_plist={
"NSHighResolutionCapable": "True",
"NSPrincipalClass": "NSApplication",
"NSRequiresAquaSystemAppearance": "False",
"CFBundleDisplayName": "ComicTagger",
"CFBundleShortVersionString": ctversion.version,
"CFBundleVersion": ctversion.version,
"CFBundleDocumentTypes": [
{
"CFBundleTypeRole": "Viewer",
"LSItemContentTypes": [
"com.rarlab.rar-archive",
],
"CFBundleTypeName": "RAR Archive",
"CFBundleTypeRole": "Editor",
"LSHandlerRank": "Default",
},
{
"CFBundleTypeRole": "Editor",
"LSHandlerRank": "Default",
"LSItemContentTypes": [
"public.folder",
],
"CFBundleTypeName": "Folder",
},
{
"CFBundleTypeExtensions": [
"cbz",
],
"LSTypeIsPackage": False,
"NSPersistentStoreTypeKey": "Binary",
"CFBundleTypeIconSystemGenerated": True,
"CFBundleTypeName": "ZIP Comic Archive",
# 'CFBundleTypeIconFile': 'cbz',
"LSItemContentTypes": [
"public.zip-comic-archive",
"com.simplecomic.cbz-archive",
"com.macitbetter.cbz-archive",
"public.cbz-archive",
"cx.c3.cbz-archive",
"com.yacreader.yacreader.cbz",
"com.milke.cbz-archive",
"com.bitcartel.comicbooklover.cbz",
"public.archive.cbz",
],
"CFBundleTypeRole": "Editor",
"LSHandlerRank": "Default",
},
{
"CFBundleTypeExtensions": [
"cb7",
],
"LSTypeIsPackage": False,
"NSPersistentStoreTypeKey": "Binary",
"CFBundleTypeIconSystemGenerated": True,
"CFBundleTypeName": "7-Zip Comic Archive",
# 'CFBundleTypeIconFile': 'cb7',
"LSItemContentTypes": [
"com.simplecomic.cb7-archive",
"public.cb7-archive",
"com.macitbetter.cb7-archive",
"cx.c3.cb7-archive",
"org.7-zip.7-zip-comic-archive",
],
"CFBundleTypeRole": "Editor",
"LSHandlerRank": "Default",
},
{
"CFBundleTypeExtensions": [
"cbr",
],
"LSTypeIsPackage": False,
"NSPersistentStoreTypeKey": "Binary",
"CFBundleTypeIconSystemGenerated": True,
"CFBundleTypeName": "RAR Comic Archive",
# 'CFBundleTypeIconFile': 'cbr',
"LSItemContentTypes": [
"com.rarlab.rar-comic-archive",
"com.simplecomic.cbr-archive",
"com.macitbetter.cbr-archive",
"public.cbr-archive",
"cx.c3.cbr-archive",
"com.bitcartel.comicbooklover.cbr",
"com.milke.cbr-archive",
"public.archive.cbr",
"com.yacreader.yacreader.cbr",
],
"CFBundleTypeRole": "Editor",
"LSHandlerRank": "Default",
},
],
"UTImportedTypeDeclarations": [
{
"UTTypeIdentifier": "com.rarlab.rar-archive",
"UTTypeDescription": "RAR Archive",
"UTTypeConformsTo": [
"public.data",
"public.archive",
],
"UTTypeTagSpecification": {
"public.mime-type": [
"application/x-rar",
"application/x-rar-compressed",
],
"public.filename-extension": [
"rar",
],
},
},
{
# 'UTTypeIcons': {
# 'UTTypeIconText': 'cbr',
# 'UTTypeIconBackgroundName': comic-fill
# }
"UTTypeConformsTo": [
"public.data",
"public.archive",
"com.rarlab.rar-archive",
],
# 'UTTypeIconFile': 'cbr',
"UTTypeIdentifier": "com.rarlab.rar-comic-archive",
"UTTypeDescription": "RAR Comic Archive",
"UTTypeTagSpecification": {
"public.mime-type": [
"application/vnd.comicbook-rar",
"application/x-cbr",
],
"public.filename-extension": [
"cbr",
],
},
},
{
# 'UTTypeIcons': {
# 'UTTypeIconText': 'cbz',
# 'UTTypeIconBackgroundName': 'comic-fill',
# }
"UTTypeConformsTo": [
"public.data",
"public.archive",
"public.zip-archive",
],
# 'UTTypeIconFile': cbz,
"UTTypeIdentifier": "public.zip-comic-archive",
"UTTypeDescription": "ZIP Comic Archive",
"UTTypeTagSpecification": {
"public.filename-extension": [
"cbz",
],
},
},
{
# 'UTTypeIcons': {
# 'UTTypeIconText': 'cb7',
# 'UTTypeIconBackgroundName': comic-fill
# }
"UTTypeConformsTo": [
"public.data",
"public.archive",
"org.7-zip.7-zip-archive",
],
# 'UTTypeIconFile': cb7
"UTTypeIdentifier": "org.7-zip.7-zip-comic-archive",
"UTTypeDescription": "7-Zip Comic Archive",
"UTTypeTagSpecification": {
"public.mime-type": [
"application/vnd.comicbook+7-zip",
"application/x-cb7-compressed",
],
"public.filename-extension": [
"cb7",
],
},
},
],
},
bundle_identifier=None,
)

View File

@ -0,0 +1,11 @@
from __future__ import annotations
import os
import comicapi.__pyinstaller
def get_hook_dirs() -> list[str]:
hooks = [os.path.dirname(__file__)]
hooks.extend(comicapi.__pyinstaller.get_hook_dirs())
return hooks

View File

@ -0,0 +1,7 @@
from __future__ import annotations
from PyInstaller.utils.hooks import collect_data_files
datas = []
datas += collect_data_files("comictaggerlib.ui")
datas += collect_data_files("comictaggerlib.graphics")

View File

@ -0,0 +1,7 @@
from __future__ import annotations
import os
from PyInstaller.utils.hooks import get_module_file_attribute
datas = [(os.path.join(os.path.dirname(get_module_file_attribute("wordninja")), "wordninja"), "wordninja")]

View File

@ -4,7 +4,7 @@ import logging
from PyQt5 import QtCore, QtGui, QtWidgets, uic
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
logger = logging.getLogger(__name__)
@ -25,7 +25,7 @@ class QTextEditLogger(QtCore.QObject, logging.Handler):
class ApplicationLogWindow(QtWidgets.QDialog):
def __init__(self, log_handler: QTextEditLogger, parent: QtCore.QObject = None) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("logwindow.ui"), self)
uic.loadUi(ui_path / "logwindow.ui", self)
self.log_handler = log_handler
self.log_handler.qlog.connect(self.textEdit.append)

View File

@ -26,6 +26,7 @@ from comicapi.genericmetadata import GenericMetadata
from comictaggerlib.coverimagewidget import CoverImageWidget
from comictaggerlib.resulttypes import IssueResult, MultipleMatch
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
from comictaggerlib.ui.qtutils import reduce_widget_font_size
from comictalker.talkerbase import ComicTalker
@ -46,7 +47,7 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("matchselectionwindow.ui"), self)
uic.loadUi(ui_path / "matchselectionwindow.ui", self)
self.settings = settings

View File

@ -20,7 +20,7 @@ import logging
from PyQt5 import QtCore, QtWidgets, uic
from comictaggerlib.coverimagewidget import CoverImageWidget
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
from comictaggerlib.ui.qtutils import reduce_widget_font_size
from comictalker.talkerbase import ComicTalker
@ -31,7 +31,7 @@ class AutoTagProgressWindow(QtWidgets.QDialog):
def __init__(self, parent: QtWidgets.QWidget, talker_api: ComicTalker) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("autotagprogresswindow.ui"), self)
uic.loadUi(ui_path / "autotagprogresswindow.ui", self)
self.archiveCoverWidget = CoverImageWidget(
self.archiveCoverContainer, talker_api, CoverImageWidget.DataMode, False

View File

@ -20,6 +20,7 @@ import logging
from PyQt5 import QtCore, QtWidgets, uic
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
logger = logging.getLogger(__name__)
@ -28,7 +29,7 @@ class AutoTagStartWindow(QtWidgets.QDialog):
def __init__(self, parent: QtWidgets.QWidget, settings: ComicTaggerSettings, msg: str) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("autotagstartwindow.ui"), self)
uic.loadUi(ui_path / "autotagstartwindow.ui", self)
self.label.setText(msg)
self.setWindowFlags(

View File

@ -29,6 +29,7 @@ from comicapi.comicarchive import ComicArchive, MetaDataStyle
from comicapi.genericmetadata import GenericMetadata
from comictaggerlib.cbltransformer import CBLTransformer
from comictaggerlib.filerenamer import FileRenamer, get_rename_dir
from comictaggerlib.graphics import graphics_path
from comictaggerlib.issueidentifier import IssueIdentifier
from comictaggerlib.resulttypes import MultipleMatch, OnlineMatchResults
from comictaggerlib.settings import ComicTaggerSettings
@ -220,7 +221,7 @@ def process_file_cli(
) -> None:
batch_mode = len(opts.file_list) > 1
ca = ComicArchive(filename, settings.rar_exe_path, ComicTaggerSettings.get_graphic("nocover.png"))
ca = ComicArchive(filename, settings.rar_exe_path, str(graphics_path / "nocover.png"))
if not os.path.lexists(filename):
logger.error("Cannot find %s", filename)

View File

@ -26,10 +26,11 @@ from PyQt5 import QtCore, QtGui, QtWidgets, uic
from comicapi import utils
from comicapi.comicarchive import ComicArchive
from comictaggerlib.graphics import graphics_path
from comictaggerlib.imagefetcher import ImageFetcher
from comictaggerlib.imagepopup import ImagePopup
from comictaggerlib.pageloader import PageLoader
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
from comictaggerlib.ui.qtutils import get_qimage_from_data, reduce_widget_font_size
from comictalker.talkerbase import ComicTalker
@ -93,7 +94,7 @@ class CoverImageWidget(QtWidgets.QWidget):
super().__init__(parent)
self.cover_fetcher = ImageFetcher()
uic.loadUi(ComicTaggerSettings.get_ui_file("coverimagewidget.ui"), self)
uic.loadUi(ui_path / "coverimagewidget.ui", self)
reduce_widget_font_size(self.label)
@ -119,8 +120,8 @@ class CoverImageWidget(QtWidgets.QWidget):
self.imageCount = 1
self.imageData = b""
self.btnLeft.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("left.png")))
self.btnRight.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("right.png")))
self.btnLeft.setIcon(QtGui.QIcon(str(graphics_path / "left.png")))
self.btnRight.setIcon(QtGui.QIcon(str(graphics_path / "right.png")))
self.btnLeft.clicked.connect(self.decrement_image)
self.btnRight.clicked.connect(self.increment_image)
@ -203,7 +204,7 @@ class CoverImageWidget(QtWidgets.QWidget):
self.imageCount = len(self.url_list)
self.update_content()
# TODO No need to search for alt covers as they are now in ComicIssue data
# No need to search for alt covers as they are now in ComicIssue data
if self.talker_api.static_options.has_alt_covers:
QtCore.QTimer.singleShot(1, self.start_alt_cover_search)
@ -298,7 +299,7 @@ class CoverImageWidget(QtWidgets.QWidget):
self.page_loader = None
def load_default(self) -> None:
self.current_pixmap = QtGui.QPixmap(ComicTaggerSettings.get_graphic("nocover.png"))
self.current_pixmap = QtGui.QPixmap(str(graphics_path / "nocover.png"))
self.set_display_pixmap()
def resizeEvent(self, resize_event: QtGui.QResizeEvent) -> None:

View File

@ -20,7 +20,7 @@ from typing import Any
from PyQt5 import QtWidgets, uic
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
logger = logging.getLogger(__name__)
@ -32,7 +32,7 @@ class CreditEditorWindow(QtWidgets.QDialog):
def __init__(self, parent: QtWidgets.QWidget, mode: int, role: str, name: str, primary: bool) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("crediteditorwindow.ui"), self)
uic.loadUi(ui_path / "crediteditorwindow.ui", self)
self.mode = mode

View File

@ -20,6 +20,7 @@ import logging
from PyQt5 import QtCore, QtWidgets, uic
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
logger = logging.getLogger(__name__)
@ -34,7 +35,7 @@ class ExportWindow(QtWidgets.QDialog):
def __init__(self, parent: QtWidgets.QWidget, settings: ComicTaggerSettings, msg: str) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("exportwindow.ui"), self)
uic.loadUi(ui_path / "exportwindow.ui", self)
self.label.setText(msg)
self.setWindowFlags(

View File

@ -24,9 +24,11 @@ from PyQt5 import QtCore, QtWidgets, uic
from comicapi import utils
from comicapi.comicarchive import ComicArchive
from comictaggerlib.graphics import graphics_path
from comictaggerlib.optionalmsgdialog import OptionalMessageDialog
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.settingswindow import linuxRarHelp, macRarHelp, windowsRarHelp
from comictaggerlib.ui import ui_path
from comictaggerlib.ui.qtutils import center_window_on_parent, reduce_widget_font_size
logger = logging.getLogger(__name__)
@ -62,7 +64,7 @@ class FileSelectionList(QtWidgets.QWidget):
) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("fileselectionlist.ui"), self)
uic.loadUi(ui_path / "fileselectionlist.ui", self)
self.settings = settings
@ -279,7 +281,7 @@ class FileSelectionList(QtWidgets.QWidget):
if self.is_list_dupe(path):
return self.get_current_list_row(path)
ca = ComicArchive(path, self.settings.rar_exe_path, ComicTaggerSettings.get_graphic("nocover.png"))
ca = ComicArchive(path, self.settings.rar_exe_path, str(graphics_path / "nocover.png"))
if ca.seems_to_be_a_comic_archive():
row: int = self.twList.rowCount()

View File

@ -0,0 +1,5 @@
from __future__ import annotations
import pathlib
graphics_path = pathlib.Path(__file__).parent

View File

@ -19,7 +19,8 @@ import logging
from PyQt5 import QtCore, QtGui, QtWidgets, sip, uic
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.graphics import graphics_path
from comictaggerlib.ui import ui_path
logger = logging.getLogger(__name__)
@ -28,7 +29,7 @@ class ImagePopup(QtWidgets.QDialog):
def __init__(self, parent: QtWidgets.QWidget, image_pixmap: QtGui.QPixmap) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("imagepopup.ui"), self)
uic.loadUi(ui_path / "imagepopup.ui", self)
QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor))
@ -47,7 +48,7 @@ class ImagePopup(QtWidgets.QDialog):
# TODO: macOS denies this
screen = QtWidgets.QApplication.primaryScreen()
self.desktopBg = screen.grabWindow(sip.voidptr(0), 0, 0, screen_size.width(), screen_size.height())
bg = QtGui.QPixmap(ComicTaggerSettings.get_graphic("popup_bg.png"))
bg = QtGui.QPixmap(str(graphics_path / "popup_bg.png"))
self.clientBgPixmap = bg.scaled(
screen_size.width(),
screen_size.height(),

View File

@ -22,6 +22,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets, uic
from comicapi.issuestring import IssueString
from comictaggerlib.coverimagewidget import CoverImageWidget
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
from comictaggerlib.ui.qtutils import reduce_widget_font_size
from comictalker.resulttypes import ComicIssue
from comictalker.talkerbase import ComicTalker, TalkerError
@ -50,7 +51,7 @@ class IssueSelectionWindow(QtWidgets.QDialog):
) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("issueselectionwindow.ui"), self)
uic.loadUi(ui_path / "issueselectionwindow.ui", self)
self.coverWidget = CoverImageWidget(self.coverImageContainer, talker_api, CoverImageWidget.AltCoverMode)
gridlayout = QtWidgets.QGridLayout(self.coverImageContainer)

View File

@ -19,8 +19,7 @@ import logging
from PyQt5 import QtCore, QtWidgets, uic
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import qtutils
from comictaggerlib.ui import qtutils, ui_path
logger = logging.getLogger(__name__)
@ -29,7 +28,7 @@ class LogWindow(QtWidgets.QDialog):
def __init__(self, parent: QtWidgets.QWidget) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("logwindow.ui"), self)
uic.loadUi(ui_path / "logwindow.ui", self)
self.setWindowFlags(
QtCore.Qt.WindowType(

View File

@ -29,6 +29,7 @@ import comictalker.comictalkerapi as ct_api
from comicapi import utils
from comictaggerlib import cli
from comictaggerlib.ctversion import version
from comictaggerlib.graphics import graphics_path
from comictaggerlib.options import parse_cmd_line
from comictaggerlib.settings import ComicTaggerSettings
@ -90,6 +91,17 @@ try:
qt_exception_hook = UncaughtHook()
from comictaggerlib.taggerwindow import TaggerWindow
class Application(QtWidgets.QApplication):
openFileRequest = QtCore.pyqtSignal(QtCore.QUrl, name="openfileRequest")
def event(self, event):
if event.type() == QtCore.QEvent.FileOpen:
logger.info(event.url().toLocalFile())
self.openFileRequest.emit(event.url())
return True
return super().event(event)
except ImportError as e:
def show_exception_box(log_msg: str) -> None:
@ -169,10 +181,14 @@ def ctmain() -> None:
if opts.darkmode:
args.extend(["-platform", "windows:darkmode=2"])
args.extend(sys.argv)
app = QtWidgets.QApplication(args)
app = Application(args)
# needed to catch initial open file events (macOS)
app.openFileRequest.connect(lambda x: opts.files.append(x.toLocalFile()))
if platform.system() == "Darwin":
# Set the MacOS dock icon
app.setWindowIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("app.png")))
app.setWindowIcon(QtGui.QIcon(str(graphics_path / "app.png")))
if platform.system() == "Windows":
# For pure python, tell windows that we're not python,
@ -188,7 +204,7 @@ def ctmain() -> None:
ctypes.windll.user32.SetWindowPos(console_wnd, None, 0, 0, 0, 0, swp_hidewindow) # type: ignore[attr-defined]
if platform.system() != "Linux":
img = QtGui.QPixmap(ComicTaggerSettings.get_graphic("tags.png"))
img = QtGui.QPixmap(str(graphics_path / "tags.png"))
splash = QtWidgets.QSplashScreen(img)
splash.show()
@ -197,9 +213,12 @@ def ctmain() -> None:
try:
tagger_window = TaggerWindow(opts.files, settings, talker_api, opts=opts)
tagger_window.setWindowIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("app.png")))
tagger_window.setWindowIcon(QtGui.QIcon(str(graphics_path / "app.png")))
tagger_window.show()
# Catch open file events (macOS)
app.openFileRequest.connect(tagger_window.open_file_event)
if platform.system() != "Linux":
splash.finish(tagger_window)

View File

@ -23,7 +23,7 @@ from PyQt5 import QtCore, QtWidgets, uic
from comicapi.comicarchive import ComicArchive
from comictaggerlib.coverimagewidget import CoverImageWidget
from comictaggerlib.resulttypes import IssueResult
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
from comictaggerlib.ui.qtutils import reduce_widget_font_size
from comictalker.talkerbase import ComicTalker
@ -42,7 +42,7 @@ class MatchSelectionWindow(QtWidgets.QDialog):
) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("matchselectionwindow.ui"), self)
uic.loadUi(ui_path / "matchselectionwindow.ui", self)
self.altCoverWidget = CoverImageWidget(self.altCoverContainer, talker_api, CoverImageWidget.AltCoverMode)
gridlayout = QtWidgets.QGridLayout(self.altCoverContainer)

View File

@ -23,7 +23,8 @@ from PyQt5 import QtCore, QtGui, QtWidgets, uic
from comicapi.comicarchive import ComicArchive
from comicapi.genericmetadata import GenericMetadata
from comictaggerlib.coverimagewidget import CoverImageWidget
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.graphics import graphics_path
from comictaggerlib.ui import ui_path
from comictalker.talkerbase import ComicTalker
logger = logging.getLogger(__name__)
@ -33,7 +34,7 @@ class PageBrowserWindow(QtWidgets.QDialog):
def __init__(self, parent: QtWidgets.QWidget, talker_api: ComicTalker, metadata: GenericMetadata) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("pagebrowser.ui"), self)
uic.loadUi(ui_path / "pagebrowser.ui", self)
self.pageWidget = CoverImageWidget(self.pageContainer, talker_api, CoverImageWidget.ArchiveMode)
gridlayout = QtWidgets.QGridLayout(self.pageContainer)
@ -59,8 +60,8 @@ class PageBrowserWindow(QtWidgets.QDialog):
self.btnPrev.setText("<<")
self.btnNext.setText(">>")
else:
self.btnPrev.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("left.png")))
self.btnNext.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("right.png")))
self.btnPrev.setIcon(QtGui.QIcon(str(graphics_path / "left.png")))
self.btnNext.setIcon(QtGui.QIcon(str(graphics_path / "right.png")))
self.btnNext.clicked.connect(self.next_page)
self.btnPrev.clicked.connect(self.prev_page)

View File

@ -22,8 +22,8 @@ from PyQt5 import QtCore, QtGui, QtWidgets, uic
from comicapi.comicarchive import ComicArchive, MetaDataStyle
from comicapi.genericmetadata import ImageMetadata, PageType
from comictaggerlib.coverimagewidget import CoverImageWidget
from comictaggerlib.settings import ComicTaggerSettings
from comictalker.talkerbase import ComicTalker
from comictaggerlib.ui import ui_path
logger = logging.getLogger(__name__)
@ -71,7 +71,7 @@ class PageListEditor(QtWidgets.QWidget):
def __init__(self, parent: QtWidgets.QWidget, talker_api: ComicTalker) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("pagelisteditor.ui"), self)
uic.loadUi(ui_path / "pagelisteditor.ui", self)
self.pageWidget = CoverImageWidget(self.pageContainer, talker_api, CoverImageWidget.ArchiveMode)
gridlayout = QtWidgets.QGridLayout(self.pageContainer)

View File

@ -19,7 +19,7 @@ import logging
from PyQt5 import QtCore, QtWidgets, uic
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
from comictaggerlib.ui.qtutils import reduce_widget_font_size
logger = logging.getLogger(__name__)
@ -29,7 +29,7 @@ class IDProgressWindow(QtWidgets.QDialog):
def __init__(self, parent: QtWidgets.QWidget) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("progresswindow.ui"), self)
uic.loadUi(ui_path / "progresswindow.ui", self)
self.setWindowFlags(
QtCore.Qt.WindowType(

View File

@ -25,6 +25,7 @@ from comicapi.genericmetadata import GenericMetadata
from comictaggerlib.filerenamer import FileRenamer, get_rename_dir
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.settingswindow import SettingsWindow
from comictaggerlib.ui import ui_path
from comictaggerlib.ui.qtutils import center_window_on_parent
from comictalker.talkerbase import ComicTalker
@ -42,7 +43,7 @@ class RenameWindow(QtWidgets.QDialog):
) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("renamewindow.ui"), self)
uic.loadUi(ui_path / "renamewindow.ui", self)
self.label.setText(f"Preview (based on {MetaDataStyle.name[data_style]} tags):")
self.setWindowFlags(
@ -60,7 +61,8 @@ class RenameWindow(QtWidgets.QDialog):
self.rename_list: list[str] = []
self.btnSettings.clicked.connect(self.modify_settings)
self.renamer = FileRenamer(None, platform="universal" if self.settings.rename_strict else "auto")
platform = "universal" if self.settings.rename_strict else "auto"
self.renamer = FileRenamer(None, platform=platform)
self.do_preview()

View File

@ -20,7 +20,6 @@ import logging
import os
import pathlib
import platform
import sys
import uuid
from collections.abc import Iterator
from typing import TextIO, no_type_check
@ -42,21 +41,6 @@ class ComicTaggerSettings:
ComicTaggerSettings.folder = pathlib.Path(os.path.expanduser("~")) / ".ComicTagger"
return pathlib.Path(ComicTaggerSettings.folder)
@staticmethod
def base_dir() -> pathlib.Path:
if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"):
return pathlib.Path(sys._MEIPASS) # type: ignore[attr-defined]
return pathlib.Path(__file__).parent
@staticmethod
def get_graphic(filename: str | pathlib.Path) -> str:
return str(ComicTaggerSettings.base_dir() / "graphics" / filename)
@staticmethod
def get_ui_file(filename: str | pathlib.Path) -> pathlib.Path:
return ComicTaggerSettings.base_dir() / "ui" / filename
@staticmethod
def get_source_settings(source_name: str, source_settings: dict):
def get_type(section: str, setting: dict):

View File

@ -29,6 +29,7 @@ from comicapi.genericmetadata import md_test
from comictaggerlib.filerenamer import FileRenamer
from comictaggerlib.imagefetcher import ImageFetcher
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
from comictalker.comiccacher import ComicCacher
from comictalker.talkerbase import ComicTalker
@ -131,7 +132,7 @@ class SettingsWindow(QtWidgets.QDialog):
def __init__(self, parent: QtWidgets.QWidget, settings: ComicTaggerSettings, talker_api: ComicTalker) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("settingswindow.ui"), self)
uic.loadUi(ui_path / "settingswindow.ui", self)
self.setWindowFlags(
QtCore.Qt.WindowType(self.windowFlags() & ~QtCore.Qt.WindowType.WindowContextHelpButtonHint)
@ -548,4 +549,4 @@ class TemplateHelpWindow(QtWidgets.QDialog):
def __init__(self, parent: QtWidgets.QWidget) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("TemplateHelp.ui"), self)
uic.loadUi(ui_path / "TemplateHelp.ui", self)

View File

@ -49,6 +49,7 @@ from comictaggerlib.coverimagewidget import CoverImageWidget
from comictaggerlib.crediteditorwindow import CreditEditorWindow
from comictaggerlib.exportwindow import ExportConflictOpts, ExportWindow
from comictaggerlib.fileselectionlist import FileInfo, FileSelectionList
from comictaggerlib.graphics import graphics_path
from comictaggerlib.issueidentifier import IssueIdentifier
from comictaggerlib.logwindow import LogWindow
from comictaggerlib.optionalmsgdialog import OptionalMessageDialog
@ -58,6 +59,7 @@ from comictaggerlib.renamewindow import RenameWindow
from comictaggerlib.resulttypes import IssueResult, MultipleMatch, OnlineMatchResults
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.settingswindow import SettingsWindow
from comictaggerlib.ui import ui_path
from comictaggerlib.ui.qtutils import center_window_on_parent, reduce_widget_font_size
from comictaggerlib.versionchecker import VersionChecker
from comictaggerlib.volumeselectionwindow import VolumeSelectionWindow
@ -84,7 +86,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("taggerwindow.ui"), self)
uic.loadUi(ui_path / "taggerwindow.ui", self)
self.settings = settings
self.talker_api = talker_api
self.log_window = self.setup_logger()
@ -152,7 +154,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.scrollAreaWidgetContents.adjustSize()
self.setWindowIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("app.png")))
self.setWindowIcon(QtGui.QIcon(str(graphics_path / "app.png")))
# TODO: this needs to be looked at
if opts is not None and opts.type:
# respect the command line option tag type
@ -263,6 +265,10 @@ Have fun!
if self.settings.check_for_new_version:
pass
def open_file_event(self, url: QtCore.QUrl) -> None:
logger.info(url.toLocalFile())
self.fileSelectionList.add_path_list([url.toLocalFile()])
def sigint_handler(self, *args: Any) -> None:
# defer the actual close in the app loop thread
QtCore.QTimer.singleShot(200, lambda: execute(self.close))
@ -299,7 +305,7 @@ Have fun!
def update_app_title(self) -> None:
self.setWindowIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("app.png")))
self.setWindowIcon(QtGui.QIcon(str(graphics_path / "app.png")))
if self.comic_archive is None:
self.setWindowTitle(self.appName)
@ -420,21 +426,21 @@ Have fun!
self.actionComicTaggerForum.triggered.connect(self.show_forum)
# Notes Menu
self.btnOpenWebLink.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("open.png")))
self.btnOpenWebLink.setIcon(QtGui.QIcon(str(graphics_path / "open.png")))
# ToolBar
self.actionLoad.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("open.png")))
self.actionLoadFolder.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("longbox.png")))
self.actionWrite_Tags.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("save.png")))
self.actionParse_Filename.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("parse.png")))
self.actionParse_Filename_split_words.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("parse.png")))
self.actionSearchOnline.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("search.png")))
self.actionLiteralSearch.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("search.png")))
self.actionAutoIdentify.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("auto.png")))
self.actionAutoTag.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("autotag.png")))
self.actionAutoImprint.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("autotag.png")))
self.actionClearEntryForm.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("clear.png")))
self.actionPageBrowser.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("browse.png")))
self.actionLoad.setIcon(QtGui.QIcon(str(graphics_path / "open.png")))
self.actionLoadFolder.setIcon(QtGui.QIcon(str(graphics_path / "longbox.png")))
self.actionWrite_Tags.setIcon(QtGui.QIcon(str(graphics_path / "save.png")))
self.actionParse_Filename.setIcon(QtGui.QIcon(str(graphics_path / "parse.png")))
self.actionParse_Filename_split_words.setIcon(QtGui.QIcon(str(graphics_path / "parse.png")))
self.actionSearchOnline.setIcon(QtGui.QIcon(str(graphics_path / "search.png")))
self.actionLiteralSearch.setIcon(QtGui.QIcon(str(graphics_path / "search.png")))
self.actionAutoIdentify.setIcon(QtGui.QIcon(str(graphics_path / "auto.png")))
self.actionAutoTag.setIcon(QtGui.QIcon(str(graphics_path / "autotag.png")))
self.actionAutoImprint.setIcon(QtGui.QIcon(str(graphics_path / "autotag.png")))
self.actionClearEntryForm.setIcon(QtGui.QIcon(str(graphics_path / "clear.png")))
self.actionPageBrowser.setIcon(QtGui.QIcon(str(graphics_path / "browse.png")))
self.toolBar.addAction(self.actionLoad)
self.toolBar.addAction(self.actionLoadFolder)
@ -569,7 +575,7 @@ Have fun!
msg_box = QtWidgets.QMessageBox()
msg_box.setWindowTitle("About " + self.appName)
msg_box.setTextFormat(QtCore.Qt.TextFormat.RichText)
msg_box.setIconPixmap(QtGui.QPixmap(ComicTaggerSettings.get_graphic("about.png")))
msg_box.setIconPixmap(QtGui.QPixmap(str(graphics_path / "about.png")))
msg_box.setText(
"<br><br><br>"
+ self.appName

View File

@ -1 +1,5 @@
from __future__ import annotations
import pathlib
ui_path = pathlib.Path(__file__).parent

View File

@ -6,7 +6,7 @@ import io
import logging
import traceback
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.graphics import graphics_path
logger = logging.getLogger(__name__)
@ -77,7 +77,7 @@ if qt_available:
pass
# if still nothing, go with default image
if not success:
img.load(ComicTaggerSettings.get_graphic("nocover.png"))
img.load(str(graphics_path / "nocover.png"))
return img
def qt_error(msg: str, e: Exception | None = None) -> None:

View File

@ -31,6 +31,7 @@ from comictaggerlib.issueselectionwindow import IssueSelectionWindow
from comictaggerlib.matchselectionwindow import MatchSelectionWindow
from comictaggerlib.progresswindow import IDProgressWindow
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
from comictaggerlib.ui.qtutils import reduce_widget_font_size
from comictalker.resulttypes import ComicVolume
from comictalker.talkerbase import ComicTalker, TalkerError
@ -117,7 +118,7 @@ class VolumeSelectionWindow(QtWidgets.QDialog):
) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("volumeselectionwindow.ui"), self)
uic.loadUi(ui_path / "volumeselectionwindow.ui", self)
self.imageWidget = CoverImageWidget(self.imageContainer, talker_api, CoverImageWidget.URLMode)
gridlayout = QtWidgets.QGridLayout(self.imageContainer)

View File

@ -1 +0,0 @@
thefuzz[speedup]>=0.19.0

View File

@ -6,8 +6,8 @@ pillow>=9.1.0
py7zr
pycountry
pyicu; sys_platform == 'linux' or sys_platform == 'darwin'
rapidfuzz>=2.12.0
requests==2.*
text2digits
thefuzz>=0.19.0
typing_extensions
wordninja

View File

@ -5,7 +5,7 @@ flake8-black
flake8-encodings
flake8-isort
isort>=5.10
pyinstaller>=4.10
pyinstaller>=4.10, != 5.6
pytest==7.*
setuptools>=42
setuptools_scm[toml]>=3.4

View File

@ -57,7 +57,12 @@ setup(
url="https://github.com/comictagger/comictagger",
packages=["comictaggerlib", "comicapi"],
package_data={"comictaggerlib": ["ui/*", "graphics/*"], "comicapi": ["data/*"]},
entry_points=dict(console_scripts=["comictagger=comictaggerlib.main:ctmain"]),
entry_points=dict(
console_scripts=["comictagger=comictaggerlib.main:ctmain"],
pyinstaller40=[
"hook-dirs = comictaggerlib.__pyinstaller:get_hook_dirs",
],
),
classifiers=[
"Development Status :: 4 - Beta",
"Environment :: Console",