Compare commits

...

21 Commits
1.5.2 ... 1.5.5

Author SHA1 Message Date
e5f6a7d1d6 Add warning about settings 2022-11-25 17:09:22 -08:00
e7f937ecd2 Enable version checking 2022-11-25 17:08:26 -08:00
3f9e5457f6 Fix make clean 2022-11-24 09:41:51 -08:00
cc2ef8593c Update pre-commit 2022-11-24 01:25:24 -08:00
c5a5fc8bdb Fix issue with combine_notes 2022-11-24 01:24:15 -08:00
1cbed64299 Fix an issue with normalizing the platform in filerenamer.py 2022-11-23 12:36:19 -08:00
c608ff80a1 Improve typing 2022-11-22 17:11:56 -08:00
76fb565d4e Merge branch 'mizaki-iiemptyurl' into develop 2022-11-11 17:09:45 -08:00
880b1be401 Return zero score if there is no image url. Fixes #392 2022-11-10 16:15:27 +00:00
c469fdb25e Make 7zip support optional 2022-11-06 08:27:45 -08:00
685ce014b6 Fix tests for comicvinetalker 2022-11-04 16:27:30 -07:00
62bf1d3808 Update macOS packaging 2022-11-04 16:16:19 -07:00
d55d75cd79 Append notes instead of overwriting them
Add issue_id to GenericMetadata
2022-11-04 15:39:40 -07:00
70293a0819 Require PyInstaller >= 5.6.2 2022-11-01 13:51:10 -07:00
8592fdee74 Revert "Install PyInstaller from git until >5.6.1 is available"
This reverts commit 79137a12f8.
2022-11-01 13:49:52 -07:00
618e15600f Fix retrieving issues from cache when volume is incomplete 2022-10-29 19:21:11 -07:00
73dd33dc64 Fix tags in GitHub Actions checkout 2022-10-29 13:09:13 -07:00
3774ab0568 Force install PyInstaller from git until >5.6.1 is available 2022-10-29 11:04:46 -07:00
f8807675d6 Cache issue info 2022-10-29 11:02:21 -07:00
79137a12f8 Install PyInstaller from git until >5.6.1 is available 2022-10-29 10:10:37 -07:00
d33d274725 Fix fetching alternate cover urls (fixes #372) 2022-10-29 10:10:35 -07:00
28 changed files with 172 additions and 127 deletions

View File

@ -78,6 +78,7 @@ 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

View File

@ -1,7 +1,7 @@
exclude: ^scripts
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
@ -19,7 +19,7 @@ repos:
- id: isort
args: [--af,--add-import, 'from __future__ import annotations']
- repo: https://github.com/asottile/pyupgrade
rev: v3.1.0
rev: v3.2.2
hooks:
- id: pyupgrade
args: [--py39-plus]
@ -33,12 +33,12 @@ repos:
- id: autoflake
args: [-i]
- repo: https://github.com/PyCQA/flake8
rev: 5.0.4
rev: 6.0.0
hooks:
- id: flake8
additional_dependencies: [flake8-encodings, flake8-warnings, flake8-builtins, flake8-length, flake8-print]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.982
rev: v0.991
hooks:
- id: mypy
additional_dependencies: [types-setuptools, types-requests]

View File

@ -34,7 +34,7 @@ $(PYTHON_VENV):
$(PY3) -m venv $(VENV)
clean:
find . -maxdepth 4 -type d -name "__pycache__"
find . -maxdepth 4 -type d -name "__pycache__" -print -depth -exec rm -rf {} \;
rm -rf $(PACKAGE_PATH) $(INSTALL_STAMP) build dist MANIFEST comictaggerlib/ctversion.py
$(MAKE) -C mac clean

View File

@ -28,7 +28,6 @@ import zipfile
from typing import cast
import natsort
import py7zr
import wordninja
from comicapi import filenamelexer, filenameparser, utils
@ -37,6 +36,12 @@ from comicapi.comicbookinfo import ComicBookInfo
from comicapi.comicinfoxml import ComicInfoXml
from comicapi.genericmetadata import GenericMetadata, PageType
try:
import py7zr
z7_support = True
except ImportError:
z7_support = False
try:
from unrar.cffi import rarfile
@ -756,15 +761,13 @@ class ComicArchive:
self.archiver.path = pathlib.Path(path)
def sevenzip_test(self) -> bool:
return py7zr.is_7zfile(self.path)
return z7_support and py7zr.is_7zfile(self.path)
def zip_test(self) -> bool:
return zipfile.is_zipfile(self.path)
def rar_test(self) -> bool:
if rar_support:
return rarfile.is_rarfile(str(self.path))
return False
return rar_support and rarfile.is_rarfile(str(self.path))
def folder_test(self) -> bool:
return self.path.is_dir()

View File

@ -137,17 +137,11 @@ class ComicInfoXml:
# second, convert each list to string, and add to XML struct
assign("Writer", ", ".join(credit_writer_list))
assign("Penciller", ", ".join(credit_penciller_list))
assign("Inker", ", ".join(credit_inker_list))
assign("Colorist", ", ".join(credit_colorist_list))
assign("Letterer", ", ".join(credit_letterer_list))
assign("CoverArtist", ", ".join(credit_cover_list))
assign("Editor", ", ".join(credit_editor_list))
assign("Publisher", md.publisher)

View File

@ -90,7 +90,8 @@ class Item:
class Lexer:
def __init__(self, string: str) -> None:
self.input: str = string # The string being scanned
self.state: Callable[[Lexer], Callable | None] | None = None # The next lexing function to enter
# The next lexing function to enter
self.state: Callable[[Lexer], Callable | None] | None = None # type: ignore[type-arg]
self.pos: int = -1 # Current position in the input
self.start: int = 0 # Start position of this item
self.lastPos: int = 0 # Position of most recent item returned by nextItem
@ -172,13 +173,13 @@ class Lexer:
# Errorf returns an error token and terminates the scan by passing
# Back a nil pointer that will be the next state, terminating self.nextItem.
def errorf(lex: Lexer, message: str) -> Callable[[Lexer], Callable | None] | None:
def errorf(lex: Lexer, message: str) -> Callable[[Lexer], Callable | None] | None: # type: ignore[type-arg]
lex.items.append(Item(ItemType.Error, lex.start, message))
return None
# Scans the elements inside action delimiters.
def lex_filename(lex: Lexer) -> Callable[[Lexer], Callable | None] | None:
def lex_filename(lex: Lexer) -> Callable[[Lexer], Callable | None] | None: # type: ignore[type-arg]
r = lex.get()
if r == eof:
if lex.paren_depth != 0:
@ -257,7 +258,7 @@ def lex_filename(lex: Lexer) -> Callable[[Lexer], Callable | None] | None:
return lex_filename
def lex_operator(lex: Lexer) -> Callable:
def lex_operator(lex: Lexer) -> Callable: # type: ignore[type-arg]
lex.accept_run("-|:;")
lex.emit(ItemType.Operator)
return lex_filename
@ -265,7 +266,7 @@ def lex_operator(lex: Lexer) -> Callable:
# LexSpace scans a run of space characters.
# One space has already been seen.
def lex_space(lex: Lexer) -> Callable:
def lex_space(lex: Lexer) -> Callable: # type: ignore[type-arg]
while is_space(lex.peek()):
lex.get()
@ -274,7 +275,7 @@ def lex_space(lex: Lexer) -> Callable:
# Lex_text scans an alphanumeric.
def lex_text(lex: Lexer) -> Callable:
def lex_text(lex: Lexer) -> Callable: # type: ignore[type-arg]
while True:
r = lex.get()
if is_alpha_numeric(r):
@ -313,7 +314,7 @@ def cal(value: str) -> set[Any]:
return set(month_abbr + month_name + day_abbr + day_name)
def lex_number(lex: Lexer) -> Callable[[Lexer], Callable | None] | None:
def lex_number(lex: Lexer) -> Callable[[Lexer], Callable | None] | None: # type: ignore[type-arg]
if not lex.scan_number():
return errorf(lex, "bad number syntax: " + lex.input[lex.start : lex.pos])
# Complex number logic removed. Messes with math operations without space

View File

@ -343,7 +343,7 @@ class Parser:
remove_fcbd: bool = False,
remove_publisher: bool = False,
) -> None:
self.state: Callable[[Parser], Callable | None] | None = None
self.state: Callable[[Parser], Callable | None] | None = None # type: ignore[type-arg]
self.pos = -1
self.firstItem = True
@ -412,7 +412,7 @@ class Parser:
self.state = self.state(self)
def parse(p: Parser) -> Callable[[Parser], Callable | None] | None:
def parse(p: Parser) -> Callable[[Parser], Callable | None] | None: # type: ignore[type-arg]
item: filenamelexer.Item = p.get()
# We're done, time to do final processing
@ -670,7 +670,7 @@ def parse(p: Parser) -> Callable[[Parser], Callable | None] | None:
# TODO: What about more esoteric numbers???
def parse_issue_number(p: Parser) -> Callable[[Parser], Callable | None] | None:
def parse_issue_number(p: Parser) -> Callable[[Parser], Callable | None] | None: # type: ignore[type-arg]
item = p.input[p.pos]
if "issue" in p.filename_info:
@ -702,7 +702,7 @@ def parse_issue_number(p: Parser) -> Callable[[Parser], Callable | None] | None:
return parse
def parse_series(p: Parser) -> Callable[[Parser], Callable | None] | None:
def parse_series(p: Parser) -> Callable[[Parser], Callable | None] | None: # type: ignore[type-arg]
item = p.input[p.pos]
series: list[list[filenamelexer.Item]] = [[]]
@ -908,7 +908,7 @@ def resolve_issue(p: Parser) -> None:
p.filename_info["issue"] = p.filename_info["volume"]
def parse_finish(p: Parser) -> Callable[[Parser], Callable | None] | None:
def parse_finish(p: Parser) -> Callable[[Parser], Callable | None] | None: # type: ignore[type-arg]
resolve_year(p)
resolve_issue(p)
@ -1014,7 +1014,7 @@ def get_remainder(p: Parser) -> str:
return remainder.strip()
def parse_info_specifier(p: Parser) -> Callable[[Parser], Callable | None] | None:
def parse_info_specifier(p: Parser) -> Callable[[Parser], Callable | None] | None: # type: ignore[type-arg]
item = p.input[p.pos]
index = p.pos

View File

@ -78,6 +78,7 @@ class GenericMetadata:
is_empty: bool = True
tag_origin: str | None = None
issue_id: int | None = None
series: str | None = None
issue: str | None = None
@ -128,7 +129,7 @@ class GenericMetadata:
last_mark: str | None = None
cover_image: str | None = None
def __post_init__(self):
def __post_init__(self) -> None:
for key, value in self.__dict__.items():
if value and key != "is_empty":
self.is_empty = False

View File

@ -37,6 +37,14 @@ class UtilsVars:
already_fixed_encoding = False
def combine_notes(existing_notes: str | None, new_notes: str | None, split: str) -> str:
split_notes, split_str, untouched_notes = (existing_notes or "").rpartition(split)
if split_notes or split_str:
return (split_notes + (new_notes or "")).strip()
else:
return (untouched_notes + "\n" + (new_notes or "")).strip()
def parse_date_str(date_str: str) -> tuple[int | None, int | None, int | None]:
day = None
month = None

View File

@ -93,15 +93,6 @@ if platform.system() not in ["Windows"]:
"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",
@ -118,7 +109,6 @@ if platform.system() not in ["Windows"]:
"NSPersistentStoreTypeKey": "Binary",
"CFBundleTypeIconSystemGenerated": True,
"CFBundleTypeName": "ZIP Comic Archive",
# 'CFBundleTypeIconFile': 'cbz',
"LSItemContentTypes": [
"public.zip-comic-archive",
"com.simplecomic.cbz-archive",
@ -129,6 +119,7 @@ if platform.system() not in ["Windows"]:
"com.milke.cbz-archive",
"com.bitcartel.comicbooklover.cbz",
"public.archive.cbz",
"public.zip-archive",
],
"CFBundleTypeRole": "Editor",
"LSHandlerRank": "Default",
@ -141,8 +132,8 @@ if platform.system() not in ["Windows"]:
"NSPersistentStoreTypeKey": "Binary",
"CFBundleTypeIconSystemGenerated": True,
"CFBundleTypeName": "7-Zip Comic Archive",
# 'CFBundleTypeIconFile': 'cb7',
"LSItemContentTypes": [
"org.7-zip.7-zip-archive",
"com.simplecomic.cb7-archive",
"public.cb7-archive",
"com.macitbetter.cb7-archive",
@ -160,8 +151,8 @@ if platform.system() not in ["Windows"]:
"NSPersistentStoreTypeKey": "Binary",
"CFBundleTypeIconSystemGenerated": True,
"CFBundleTypeName": "RAR Comic Archive",
# 'CFBundleTypeIconFile': 'cbr',
"LSItemContentTypes": [
"com.rarlab.rar-archive",
"com.rarlab.rar-comic-archive",
"com.simplecomic.cbr-archive",
"com.macitbetter.cbr-archive",
@ -195,16 +186,11 @@ if platform.system() not in ["Windows"]:
},
},
{
# '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": {
@ -218,16 +204,11 @@ if platform.system() not in ["Windows"]:
},
},
{
# '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": {
@ -237,16 +218,11 @@ if platform.system() not in ["Windows"]:
},
},
{
# '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": {

View File

@ -22,11 +22,13 @@ import logging
import os
import pathlib
import sys
from datetime import datetime
from pprint import pprint
from comicapi import utils
from comicapi.comicarchive import ComicArchive, MetaDataStyle
from comicapi.genericmetadata import GenericMetadata
from comictaggerlib import ctversion
from comictaggerlib.cbltransformer import CBLTransformer
from comictaggerlib.comicvinetalker import ComicVineTalker, ComicVineTalkerException
from comictaggerlib.filerenamer import FileRenamer, get_rename_dir
@ -112,7 +114,11 @@ def display_match_set_for_choice(
if opts.overwrite:
md = cv_md
else:
md.overlay(cv_md)
notes = (
f"Tagged with ComicTagger {ctversion.version} using info from Comic Vine on"
f" {datetime.now():%Y-%m-%d %H:%M:%S}. [Issue ID {cv_md.issue_id}]"
)
md.overlay(cv_md.replace(notes=utils.combine_notes(md.notes, notes, "Tagged with ComicTagger")))
if opts.auto_imprint:
md.fix_publisher()
@ -461,7 +467,11 @@ def process_file_cli(
if opts.overwrite:
md = cv_md
else:
md.overlay(cv_md)
notes = (
f"Tagged with ComicTagger {ctversion.version} using info from Comic Vine on"
f" {datetime.now():%Y-%m-%d %H:%M:%S}. [Issue ID {cv_md.issue_id}]"
)
md.overlay(cv_md.replace(notes=utils.combine_notes(md.notes, notes, "Tagged with ComicTagger")))
if opts.auto_imprint:
md.fix_publisher()

View File

@ -450,6 +450,8 @@ class ComicCacher:
set_slots = ""
for key in data:
if data[key] is None:
continue
if keys != "":
keys += ", "

View File

@ -19,7 +19,6 @@ import json
import logging
import re
import time
from datetime import datetime
from typing import Any, Callable, cast
from urllib.parse import urlencode, urljoin, urlsplit
@ -327,10 +326,11 @@ class ComicVineTalker:
def fetch_issues_by_volume(self, series_id: int) -> list[resulttypes.CVIssuesResults]:
# before we search online, look in our cache, since we might already have this info
volume_data = self.fetch_volume_data(series_id)
cvc = ComicCacher()
cached_volume_issues_result = cvc.get_volume_issues_info(series_id, self.source_name)
if cached_volume_issues_result:
if len(cached_volume_issues_result) >= volume_data["count_of_issues"]:
return cached_volume_issues_result
params = {
@ -407,6 +407,10 @@ class ComicVineTalker:
self.repair_urls(filtered_issues_result)
cvc = ComicCacher()
for c in filtered_issues_result:
cvc.add_volume_issues_info(self.source_name, c["volume"]["id"], [c])
return filtered_issues_result
def fetch_issue_data(self, series_id: int, issue_number: str, settings: ComicTaggerSettings) -> GenericMetadata:
@ -461,6 +465,8 @@ class ComicVineTalker:
# Now, map the Comic Vine data to generic metadata
metadata = GenericMetadata()
metadata.is_empty = False
metadata.tag_origin = "Comic Vine"
metadata.issue_id = issue_results["id"]
metadata.series = utils.xlate(issue_results["volume"]["name"])
metadata.issue = IssueString(issue_results["issue_number"]).as_string()
@ -474,10 +480,6 @@ class ComicVineTalker:
if settings.use_series_start_as_volume:
metadata.volume = int(volume_results["start_year"])
metadata.notes = (
f"Tagged with ComicTagger {ctversion.version} using info from Comic Vine on"
f" {datetime.now():%Y-%m-%d %H:%M:%S}. [Issue ID {issue_results['id']}]"
)
metadata.web_link = issue_results["site_detail_url"]
person_credits = issue_results["person_credits"]
@ -671,7 +673,7 @@ class ComicVineTalker:
def fetch_alternate_cover_urls(self, issue_id: int, issue_page_url: str) -> list[str]:
url_list = self.fetch_cached_alternate_cover_urls(issue_id)
if url_list is not None:
if url_list:
return url_list
# scrape the CV issue page URL to get the alternate cover URLs

View File

@ -221,7 +221,7 @@ class FileRenamer:
self.template = template
def determine_name(self, ext: str) -> str:
class Default(dict):
class Default(dict[str, Any]):
def __missing__(self, key: str) -> str:
return "{" + key + "}"

View File

@ -251,6 +251,10 @@ class IssueIdentifier:
# local_cover_hash_list is a list of pre-calculated hashes.
# use_remote_alternates - indicates to use alternate covers from CV
# If there is no URL return 0
if not primary_img_url:
return Score(score=0, url="", hash=0)
try:
url_image_data = ImageFetcher().fetch(primary_thumb_url, blocking=True)
except ImageFetcherException as e:

View File

@ -94,7 +94,7 @@ try:
class Application(QtWidgets.QApplication):
openFileRequest = QtCore.pyqtSignal(QtCore.QUrl, name="openfileRequest")
def event(self, event):
def event(self, event: QtCore.QEvent) -> bool:
if event.type() == QtCore.QEvent.FileOpen:
logger.info(event.url().toLocalFile())
self.openFileRequest.emit(event.url())
@ -145,7 +145,17 @@ def ctmain() -> None:
format="%(asctime)s | %(name)s | %(levelname)s | %(message)s",
datefmt="%Y-%m-%dT%H:%M:%S",
)
# Need to load setting before anything else
if settings.settings_warning < 4:
print( # noqa: T201
"""
!!!Warning!!!
The next release will save settings in a different format
NO SETTINGS WILL BE TRANSFERED to the new version.
See https://github.com/comictagger/comictagger/releases/1.5.5 for more information.
""",
file=sys.stderr,
)
# manage the CV API key
# None comparison is used so that the empty string can unset the value

View File

@ -70,6 +70,7 @@ class ComicTaggerSettings:
# Show/ask dialog flags
self.ask_about_cbi_in_rar = True
self.show_disclaimer = True
self.settings_warning = 0
self.dont_notify_about_this_version = ""
self.ask_about_usage_stats = True
@ -158,9 +159,9 @@ class ComicTaggerSettings:
# make sure rar program is now in the path for the rar class
utils.add_to_path(os.path.dirname(self.rar_exe_path))
def reset(self):
def reset(self) -> None:
os.unlink(self.settings_file)
self.__init__(ComicTaggerSettings.folder)
self.__init__(ComicTaggerSettings.folder) # type: ignore[misc]
def load(self) -> None:
def readline_generator(f: TextIO) -> Iterator[str]:
@ -223,6 +224,8 @@ class ComicTaggerSettings:
self.ask_about_cbi_in_rar = self.config.getboolean("dialogflags", "ask_about_cbi_in_rar")
if self.config.has_option("dialogflags", "show_disclaimer"):
self.show_disclaimer = self.config.getboolean("dialogflags", "show_disclaimer")
if self.config.has_option("dialogflags", "settings_warning"):
self.settings_warning = self.config.getint("dialogflags", "settings_warning")
if self.config.has_option("dialogflags", "dont_notify_about_this_version"):
self.dont_notify_about_this_version = self.config.get("dialogflags", "dont_notify_about_this_version")
if self.config.has_option("dialogflags", "ask_about_usage_stats"):
@ -349,6 +352,7 @@ class ComicTaggerSettings:
self.config.set("dialogflags", "ask_about_cbi_in_rar", self.ask_about_cbi_in_rar)
self.config.set("dialogflags", "show_disclaimer", self.show_disclaimer)
self.config.set("dialogflags", "settings_warning", self.settings_warning)
self.config.set("dialogflags", "dont_notify_about_this_version", self.dont_notify_about_this_version)
self.config.set("dialogflags", "ask_about_usage_stats", self.ask_about_usage_stats)

View File

@ -27,6 +27,7 @@ import re
import sys
import webbrowser
from collections.abc import Iterable
from datetime import datetime
from typing import Any, Callable, cast
from urllib.parse import urlparse
@ -260,8 +261,25 @@ Have fun!
)
self.settings.show_disclaimer = not checked
if self.settings.settings_warning < 4:
checked = OptionalMessageDialog.msg(
self,
"Warning!",
f"""<span style="font-size:15px">
{"&nbsp;"*100}
The next release will save settings in a different format
<span style="font-weight: bold;font-size:19px">no settings will be transfered</span> to the new version.<br/>
See <a href="https://github.com/comictagger/comictagger/releases/1.5.5">https://github.com/comictagger/comictagger/releases/1.5.5</a>
for more information
<br/><br/>
You have {4-self.settings.settings_warning} warnings left.
</span>""",
)
if checked:
self.settings.settings_warning += 1
if self.settings.check_for_new_version:
pass
self.check_latest_version_online()
def open_file_event(self, url: QtCore.QUrl) -> None:
logger.info(url.toLocalFile())
@ -1090,7 +1108,15 @@ Have fun!
if self.settings.clear_form_before_populating_from_cv:
self.clear_form()
self.metadata.overlay(new_metadata)
notes = (
f"Tagged with ComicTagger {ctversion.version} using info from Comic Vine on"
f" {datetime.now():%Y-%m-%d %H:%M:%S}. [Issue ID {new_metadata.issue_id}]"
)
self.metadata.overlay(
new_metadata.replace(
notes=utils.combine_notes(self.metadata.notes, notes, "Tagged with ComicTagger")
)
)
# Now push the new combined data into the edit controls
self.metadata_to_form()
else:
@ -1794,7 +1820,11 @@ Have fun!
if dlg.cbxRemoveMetadata.isChecked():
md = cv_md
else:
md.overlay(cv_md)
notes = (
f"Tagged with ComicTagger {ctversion.version} using info from Comic Vine on"
f" {datetime.now():%Y-%m-%d %H:%M:%S}. [Issue ID {cv_md.issue_id}]"
)
md.overlay(cv_md.replace(notes=utils.combine_notes(md.notes, notes, "Tagged with ComicTagger")))
if self.settings.auto_imprint:
md.fix_publisher()
@ -2108,19 +2138,19 @@ Have fun!
version_checker.get_latest_version(self.settings.install_id, self.settings.send_usage_stats)
)
def version_check_complete(self, new_version: str) -> None:
if new_version not in (self.version, self.settings.dont_notify_about_this_version):
def version_check_complete(self, new_version: tuple[str, str]) -> None:
if new_version[0] not in (self.version, self.settings.dont_notify_about_this_version):
website = "https://github.com/comictagger/comictagger"
checked = OptionalMessageDialog.msg(
self,
"New version available!",
f"New version ({new_version}) available!<br>(You are currently running {self.version})<br><br>"
f"Visit <a href='{website}'>{website}</a> for more info.<br><br>",
f"New version ({new_version[1]}) available!<br>(You are currently running {self.version})<br><br>"
f"Visit <a href='{website}/releases/latest'>{website}/releases/latest</a> for more info.<br><br>",
False,
"Don't tell me about this version again",
)
if checked:
self.settings.dont_notify_about_this_version = new_version
self.settings.dont_notify_about_this_version = new_version[0]
def on_incoming_socket_connection(self) -> None:
# Accept connection from other instance.

View File

@ -16,8 +16,6 @@
from __future__ import annotations
import logging
import platform
import sys
import requests
@ -29,31 +27,23 @@ logger = logging.getLogger(__name__)
class VersionChecker:
def get_request_url(self, uuid: str, use_stats: bool) -> tuple[str, dict[str, str]]:
base_url = "http://comictagger1.appspot.com/latest"
params = {}
if use_stats:
params = {"uuid": uuid, "version": ctversion.version}
if platform.system() == "Windows":
params["platform"] = "win"
elif platform.system() == "Linux":
params["platform"] = "lin"
elif platform.system() == "Darwin":
params["platform"] = "mac"
else:
params["platform"] = "other"
if not getattr(sys, "frozen", None):
params["src"] = "T"
base_url = "https://api.github.com/repos/comictagger/comictagger/releases/latest"
params: dict[str, str] = {}
return base_url, params
def get_latest_version(self, uuid: str, use_stats: bool = True) -> str:
def get_latest_version(self, uuid: str, use_stats: bool = True) -> tuple[str, str]:
try:
url, params = self.get_request_url(uuid, use_stats)
new_version = requests.get(url, params=params).text
release = requests.get(
url,
params=params,
headers={"user-agent": "comictagger/" + ctversion.version},
).json()
except Exception:
return ""
return ("", "")
new_version = release["tag_name"]
if new_version is None or new_version == "":
return ""
return new_version.strip()
return ("", "")
return (new_version.strip(), release["name"])

View File

@ -7,7 +7,7 @@ import subprocess
import sys
def _lang_code_mac():
def _lang_code_mac() -> str:
"""
stolen from https://github.com/mu-editor/mu
Returns the user's language preference as defined in the Language & Region
@ -38,13 +38,13 @@ def _lang_code_mac():
return lang_code
def configure_locale():
def configure_locale() -> None:
if sys.platform == "darwin" and "LANG" not in os.environ:
code = _lang_code_mac()
if code != "":
os.environ["LANG"] = f"{code}.utf-8"
locale.setlocale(locale.LC_ALL, "")
sys.stdout.reconfigure(encoding=sys.getdefaultencoding())
sys.stderr.reconfigure(encoding=sys.getdefaultencoding())
sys.stdin.reconfigure(encoding=sys.getdefaultencoding())
sys.stdout.reconfigure(encoding=sys.getdefaultencoding()) # type: ignore[attr-defined]
sys.stderr.reconfigure(encoding=sys.getdefaultencoding()) # type: ignore[attr-defined]
sys.stdin.reconfigure(encoding=sys.getdefaultencoding()) # type: ignore[attr-defined]

1
requirements-7Z.txt Normal file
View File

@ -0,0 +1 @@
py7zr

View File

@ -3,7 +3,6 @@ importlib_metadata>=3.3.0
natsort>=8.1.0
pathvalidate
pillow>=9.1.0
py7zr
pycountry
pyicu; sys_platform == 'linux' or sys_platform == 'darwin'
rapidfuzz>=2.12.0

View File

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

View File

@ -160,7 +160,8 @@ date = comictaggerlib.comicvinetalker.ComicVineTalker().parse_date_str(cv_issue_
cv_md = comicapi.genericmetadata.GenericMetadata(
is_empty=False,
tag_origin=None,
tag_origin="Comic Vine",
issue_id=cv_issue_result["results"]["id"],
series=cv_issue_result["results"]["volume"]["name"],
issue=cv_issue_result["results"]["issue_number"],
title=cv_issue_result["results"]["name"],
@ -182,7 +183,7 @@ cv_md = comicapi.genericmetadata.GenericMetadata(
alternate_number=None,
alternate_count=None,
imprint=None,
notes="Tagged with ComicTagger 1.4.4a9.dev20 using info from Comic Vine on 2022-07-11 17:42:41. [Issue ID 140529]",
notes=None,
web_link=cv_issue_result["results"]["site_detail_url"],
format=None,
manga=None,

View File

@ -119,8 +119,11 @@ def test_invalid_zip(tmp_comic):
archivers = [
comicapi.comicarchive.ZipArchiver,
comicapi.comicarchive.SevenZipArchiver,
comicapi.comicarchive.FolderArchiver,
pytest.param(
comicapi.comicarchive.SevenZipArchiver,
marks=pytest.mark.xfail(not (comicapi.comicarchive.z7_support), reason="7z support"),
),
pytest.param(
comicapi.comicarchive.RarArchiver,
marks=pytest.mark.xfail(not (comicapi.comicarchive.rar_support and shutil.which("rar")), reason="rar support"),

View File

@ -39,7 +39,7 @@ def test_fetch_issues_by_volume(comicvine_api, comic_cache):
assert results == cache_issues
def test_fetch_issue_data_by_issue_id(comicvine_api, settings, mock_now, mock_version):
def test_fetch_issue_data_by_issue_id(comicvine_api, settings, mock_version):
ct = comictaggerlib.comicvinetalker.ComicVineTalker()
result = ct.fetch_issue_data_by_issue_id(140529, settings)
assert result == testing.comicvine.cv_md
@ -65,13 +65,13 @@ cv_issue = [
@pytest.mark.parametrize("volume_id, issue_number, expected", cv_issue)
def test_fetch_issue_data(comicvine_api, settings, mock_now, mock_version, volume_id, issue_number, expected):
def test_fetch_issue_data(comicvine_api, settings, mock_version, volume_id, issue_number, expected):
ct = comictaggerlib.comicvinetalker.ComicVineTalker()
results = ct.fetch_issue_data(volume_id, issue_number, settings)
assert results == expected
def test_fetch_issue_select_details(comicvine_api, mock_now, mock_version):
def test_fetch_issue_select_details(comicvine_api, mock_version):
ct = comictaggerlib.comicvinetalker.ComicVineTalker()
result = ct.fetch_issue_select_details(140529)
expected = {

View File

@ -1,7 +1,6 @@
from __future__ import annotations
import copy
import datetime
import io
import shutil
import unittest.mock
@ -115,18 +114,6 @@ def comicvine_api(monkeypatch, cbz, comic_cache) -> unittest.mock.Mock:
return m_get
@pytest.fixture
def mock_now(monkeypatch):
class mydatetime:
time = datetime.datetime(2022, 7, 11, 17, 42, 41)
@classmethod
def now(cls):
return cls.time
monkeypatch.setattr(comictaggerlib.comicvinetalker, "datetime", mydatetime)
@pytest.fixture
def mock_version(monkeypatch):
version = "1.4.4a9.dev20"

View File

@ -77,6 +77,24 @@ def test_get_language_iso(value, result):
assert result == comicapi.utils.get_language_iso(value)
combine_values = [
("hello", "english", "en", "hello\nenglish"),
("hello en", "english", "en", "hello english"),
("hello en goodbye", "english", "en", "hello english"),
("hello en en goodbye", "english", "en", "hello en english"),
("", "english", "en", "english"),
(None, "english", "en", "english"),
("hello", "", "en", "hello"),
("hello", None, "en", "hello"),
("hello", "hello", "hel", "hello"),
]
@pytest.mark.parametrize("existing_notes, new_notes, split, result", combine_values)
def test_combine_notes(existing_notes, new_notes, split, result):
assert result == comicapi.utils.combine_notes(existing_notes, new_notes, split)
def test_unique_file(tmp_path):
file = tmp_path / "test.cbz"
assert file == comicapi.utils.unique_file(file)