Compare commits

...

23 Commits
1.4.6 ... 1.5.1

Author SHA1 Message Date
2daf9b3ed8 Style and typo fixes 2022-10-04 16:15:55 -07:00
a6d55cd21a Update MetadataFormatter
Several custom conversions (the s in {title!s}) have been created
u - str.upper()
l - str.casefold()
S - str.swapcase()
t - str.title()
c - str.Capitalize()

A new syntax has been added '{title+str}' and '{title-str}':
The + indicates an alternate value.
The - indicates a default value.

If the title of a comic is not set then
'{title-str}' will output 'str'
and
'{title+str} will output ''

If the title of a comic is 'hello' then
'{title+str}' will output 'str'
and
'{title-str}' will output 'hello'
2022-10-04 16:15:20 -07:00
4034123e6d Fix rar tests again 2022-10-02 21:47:07 -07:00
5587bfac31 Fix rar tests 2022-10-02 21:13:26 -07:00
4b6d35fd3a Fix CBL tagging 2022-10-02 19:33:12 -07:00
3cf75cf2ec Update importlib_matadata usage and requirements 2022-09-19 22:54:48 -07:00
30dbe758d4 Fix windows tests 2022-09-19 22:52:45 -07:00
55384790f8 Forcefully raise an OSError on windows 2022-09-17 01:59:15 -07:00
acaf5ed510 Fix issues with renaming
Stop a crash when renaming
Properly handle replacements on linux/macos
2022-09-17 01:28:26 -07:00
d213db3129 Use correct syntax for pips --no-binary flag 2022-09-15 22:09:04 -07:00
6a717377df Automatically set release name from tag message 2022-09-10 22:35:30 -07:00
904561fb8e Merge branch 'pyicu' into develop 2022-09-10 21:48:04 -07:00
be6b71dec7 Put unix specific commands in OS specific blocks 2022-09-10 21:11:48 -07:00
63b654a173 Update ci to install pyicu 2022-09-10 19:51:26 -07:00
bc25acde9f Fix sorting
Switch natsort to use os_sorted
Remove directories when returning a list of files in a comic
Update tests to account for '!cover.jpg'
2022-09-10 19:48:50 -07:00
03677ce4b8 Fix renaming
Make ComicArchive.path always absolute
Fix unique_file not preserving the extension
Fix incorrect output when renaming in CLI mode
Fix handling of platform when renaming
2022-08-19 20:20:37 -07:00
535afcb4c6 Fix replacements 2022-08-19 19:59:58 -07:00
06255f7848 Perform replacements on literal text and format values 2022-08-18 13:48:23 -07:00
00e649bb4c Move colon handling when renaming to the MetadataFormatter class
Fixes #356
2022-08-17 16:16:38 -07:00
078f569ec6 Fix codeblock in README.md 2022-08-14 10:51:08 -07:00
315cf7d920 Merge pull request #355 from Xav83/patch-1
Adds the Chocolatey package as a way to install ComicTagger
2022-08-14 10:47:24 -07:00
e9cc6a16a8 Note that @Xav83 is the maintainer of the chocolatey package
Co-authored-by: Xavier Jouvenot <x.jouvenot@gmail.com>
2022-08-14 10:45:51 -07:00
26eb6985fe Adds the Chocolatey package as a way to install ComicTagger
Adds the Chocolatey package in the list of possibilities to install ComicTagger
2022-08-13 11:52:09 +02:00
22 changed files with 359 additions and 76 deletions

View File

@ -80,6 +80,20 @@ jobs:
run: |
choco install -y zip
if: runner.os == 'Windows'
- name: Install macos dependencies
run: |
brew install icu4c pkg-config
export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig";
export PATH="/usr/local/opt/icu4c/bin:/usr/local/opt/icu4c/sbin:$PATH"
python -m pip install --no-binary=pyicu pyicu
if: runner.os == 'macOS'
- name: Install linux dependencies
run: |
sudo apt-get install pkg-config libicu-dev
export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig";
export PATH="/usr/local/opt/icu4c/bin:/usr/local/opt/icu4c/sbin:$PATH"
python -m pip install --no-binary=pyicu pyicu
if: runner.os == 'Linux'
- name: Build and install PyPi packages
run: |

View File

@ -41,12 +41,25 @@ jobs:
run: |
choco install -y zip
if: runner.os == 'Windows'
- name: Install macos dependencies
run: |
brew install icu4c pkg-config
export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig";
export PATH="/usr/local/opt/icu4c/bin:/usr/local/opt/icu4c/sbin:$PATH"
python -m pip install --no-binary=pyicu pyicu
if: runner.os == 'macOS'
- name: Install linux dependencies
run: |
sudo apt-get install pkg-config libicu-dev
export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig";
export PATH="/usr/local/opt/icu4c/bin:/usr/local/opt/icu4c/sbin:$PATH"
python -m pip install --no-binary=pyicu pyicu
if: runner.os == 'Linux'
- name: Build, Install and Test PyPi packages
run: |
make clean pydist
python -m pip install "dist/$(python setup.py --fullname)-py3-none-any.whl[all]"
echo "CT_FULL_NAME=$(python setup.py --fullname)" >> $GITHUB_ENV
python -m flake8
python -m pytest
@ -61,12 +74,19 @@ jobs:
run: |
make dist
- name: Get release name
if: startsWith(github.ref, 'refs/tags/')
shell: bash
run: |
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
prerelease: "${{ contains(github.ref, '-') }}" # alpha-releases should be 1.3.0-alpha.x full releases should be 1.3.0
draft: false
files: |
dist/!(*Linux).zip
dist/*.whl
dist/*${{ fromJSON('["never", ""]')[runner.os == 'Linux'] }}.whl

View File

@ -2,6 +2,7 @@
[![GitHub release (latest by date)](https://img.shields.io/github/downloads/comictagger/comictagger/latest/total)](https://github.com/comictagger/comictagger/releases/latest)
[![PyPI](https://img.shields.io/pypi/v/comictagger)](https://pypi.org/project/comictagger/)
[![PyPI - Downloads](https://img.shields.io/pypi/dm/comictagger)](https://pypistats.org/packages/comictagger)
[![Chocolatey package](https://img.shields.io/chocolatey/dt/comictagger?color=blue&label=chocolatey)](https://community.chocolatey.org/packages/comictagger)
[![PyPI - License](https://img.shields.io/pypi/l/comictagger)](https://opensource.org/licenses/Apache-2.0)
[![GitHub Discussions](https://img.shields.io/github/discussions/comictagger/comictagger)](https://github.com/comictagger/comictagger/discussions)
@ -48,6 +49,12 @@ A pip package is provided, you can install it with:
There are two optional dependencies GUI and CBR. You can install the optional dependencies by specifying one or more of `GUI`,`CBR` or `all` in braces e.g. `comictagger[CBR,GUI]`
### Chocolatey installation (Windows only)
A [Chocolatey package](https://community.chocolatey.org/packages/comictagger), maintained by @Xav83, is provided, you can install it with:
```powershell
choco install comictagger
```
### From source
1. Ensure you have python 3.9 installed

View File

@ -88,9 +88,9 @@ class CoMet:
assign("readingDirection", "rtl")
if md.year is not None:
date_str = str(md.year).zfill(4)
date_str = f"{md.year:04}"
if md.month is not None:
date_str += "-" + str(md.month).zfill(2)
date_str += f"-{md.month:02}"
assign("date", date_str)
assign("coverImage", md.cover_image)

View File

@ -148,7 +148,7 @@ class SevenZipArchiver(UnknownArchiver):
def get_filename_list(self) -> list[str]:
try:
with py7zr.SevenZipFile(self.path, "r") as zf:
namelist: list[str] = zf.getnames()
namelist: list[str] = [file.filename for file in zf.list() if not file.is_directory]
return namelist
except (py7zr.Bad7zFile, OSError) as e:
@ -248,7 +248,7 @@ class ZipArchiver(UnknownArchiver):
def get_filename_list(self) -> list[str]:
try:
with zipfile.ZipFile(self.path, mode="r") as zf:
namelist = zf.namelist()
namelist = [file.filename for file in zf.infolist() if not file.is_dir()]
return namelist
except (zipfile.BadZipfile, OSError) as e:
logger.error("Error listing files in zip archive [%s]: %s", e, self.path)
@ -379,14 +379,15 @@ class RarArchiver(UnknownArchiver):
def get_comment(self) -> str:
rarc = self.get_rar_obj()
return str(rarc.comment) if rarc else ""
return rarc.comment.decode("utf-8") if rarc else ""
def set_comment(self, comment: str) -> bool:
if rar_support and self.rar_exe_path:
try:
# write comment to temp file
with tempfile.NamedTemporaryFile() as tmp_file:
tmp_file.write(comment.encode("utf-8"))
with tempfile.TemporaryDirectory() as tmp_dir:
tmp_file = pathlib.Path(tmp_dir) / "rar_comment.txt"
tmp_file.write_text(comment, encoding="utf-8")
working_dir = os.path.dirname(os.path.abspath(self.path))
@ -396,7 +397,7 @@ class RarArchiver(UnknownArchiver):
"c",
f"-w{working_dir}",
"-c-",
f"-z{tmp_file.name}",
f"-z{tmp_file}",
str(self.path),
]
subprocess.run(
@ -683,7 +684,7 @@ class ComicArchive:
self._has_cbi: bool | None = None
self._has_cix: bool | None = None
self._has_comet: bool | None = None
self.path = pathlib.Path(path)
self.path = pathlib.Path(path).absolute()
self.page_count: int | None = None
self.page_list: list[str] = []
@ -934,7 +935,7 @@ class ComicArchive:
# seems like some archive creators are on Windows, and don't know about case-sensitivity!
if sort_list:
files = cast(list[str], natsort.natsorted(files, alg=natsort.ns.IC | natsort.ns.I | natsort.ns.U))
files = cast(list[str], natsort.os_sorted(files))
# make a sub-list of image files
self.page_list = []

View File

@ -112,8 +112,8 @@ class ComicBookInfo:
metadata.credits = []
# need the language string to be ISO
if metadata.language is not None:
metadata.language = utils.get_language(metadata.language)
if metadata.language:
metadata.language = utils.get_language_iso(metadata.language)
metadata.is_empty = False

View File

@ -32,10 +32,13 @@ from text2digits import text2digits
from comicapi import filenamelexer, issuestring
logger = logging.getLogger(__name__)
t2d = text2digits.Text2Digits(add_ordinal_ending=False)
t2do = text2digits.Text2Digits(add_ordinal_ending=True)
logger = logging.getLogger(__name__)
placeholders_no_dashes = [re.compile(r"[-_]"), re.compile(r" +")]
placeholders_allow_dashes = [re.compile(r"[_]"), re.compile(r" +")]
class FileNameParser:
@ -54,9 +57,9 @@ class FileNameParser:
def fix_spaces(self, string: str, remove_dashes: bool = True) -> str:
if remove_dashes:
placeholders = [r"[-_]", r" +"]
placeholders = placeholders_no_dashes
else:
placeholders = [r"[_]", r" +"]
placeholders = placeholders_allow_dashes
for ph in placeholders:
string = re.sub(ph, self.repl, string)
return string

View File

@ -167,12 +167,12 @@ def titles_match(search_title: str, record_title: str, threshold: int = 90) -> b
def unique_file(file_name: pathlib.Path) -> pathlib.Path:
name = file_name.name
name = file_name.stem
counter = 1
while True:
if not file_name.exists():
return file_name
file_name = file_name.with_name(name + " (" + str(counter) + ")")
file_name = file_name.with_stem(name + " (" + str(counter) + ")")
counter += 1
@ -193,18 +193,15 @@ def get_language_from_iso(iso: str | None) -> str | None:
return languages[iso]
def get_language(string: str | None) -> str | None:
def get_language_iso(string: str | None) -> str | None:
if string is None:
return None
string = string.casefold()
lang = string.casefold()
lang = get_language_from_iso(string)
if lang is None:
try:
return str(pycountry.languages.lookup(string).name)
except LookupError:
return None
try:
return getattr(pycountry.languages.lookup(string), "alpha_2", None)
except LookupError:
pass
return lang

View File

@ -472,7 +472,7 @@ def process_file_cli(
match_results.good_matches.append(str(ca.path.absolute()))
elif opts.rename:
original_path = ca.path
msg_hdr = ""
if batch_mode:
msg_hdr = f"{ca.path}: "
@ -525,11 +525,14 @@ def process_file_cli(
suffix = ""
if not opts.dryrun:
# rename the file
ca.rename(utils.unique_file(full_path))
try:
ca.rename(utils.unique_file(full_path))
except OSError:
logger.exception("Failed to rename comic archive: %s", ca.path)
else:
suffix = " (dry-run, no change)"
print(f"renamed '{os.path.basename(ca.path)}' -> '{new_name}' {suffix}")
print(f"renamed '{original_path.name}' -> '{new_name}' {suffix}")
elif opts.export_to_zip:
msg_hdr = ""

View File

@ -20,10 +20,9 @@ import logging
import os
import pathlib
import string
import sys
from typing import Any, cast
from typing import Any, NamedTuple, cast
from pathvalidate import sanitize_filename
from pathvalidate import Platform, normalize_platform, sanitize_filename
from comicapi.comicarchive import ComicArchive
from comicapi.genericmetadata import GenericMetadata
@ -32,6 +31,31 @@ from comicapi.issuestring import IssueString
logger = logging.getLogger(__name__)
class Replacement(NamedTuple):
find: str
replce: str
strict_only: bool
class Replacements(NamedTuple):
literal_text: list[Replacement]
format_value: list[Replacement]
REPLACEMENTS = Replacements(
literal_text=[
Replacement(": ", " - ", True),
Replacement(":", "-", True),
],
format_value=[
Replacement(": ", " - ", True),
Replacement(":", "-", True),
Replacement("/", "-", False),
Replacement("\\", "-", True),
],
)
def get_rename_dir(ca: ComicArchive, rename_dir: str | pathlib.Path | None) -> pathlib.Path:
folder = ca.path.parent.absolute()
if rename_dir is not None:
@ -42,16 +66,55 @@ def get_rename_dir(ca: ComicArchive, rename_dir: str | pathlib.Path | None) -> p
class MetadataFormatter(string.Formatter):
def __init__(self, smart_cleanup: bool = False, platform: str = "auto") -> None:
def __init__(
self, smart_cleanup: bool = False, platform: str = "auto", replacements: Replacements = REPLACEMENTS
) -> None:
super().__init__()
self.smart_cleanup = smart_cleanup
self.platform = platform
self.platform = normalize_platform(platform)
self.replacements = replacements
def format_field(self, value: Any, format_spec: str) -> str:
if value is None or value == "":
return ""
return cast(str, super().format_field(value, format_spec))
def convert_field(self, value: Any, conversion: str) -> str:
if conversion == "u":
return str(value).upper()
if conversion == "l":
return str(value).casefold()
if conversion == "c":
return str(value).capitalize()
if conversion == "S":
return str(value).swapcase()
if conversion == "t":
return str(value).title()
return cast(str, super().convert_field(value, conversion))
def handle_replacements(self, string: str, replacements: list[Replacement]) -> str:
for find, replace, strict_only in replacements:
if self.is_strict() or not strict_only:
string = string.replace(find, replace)
return string
def none_replacement(self, value: Any, replacement: str, r: str) -> Any:
if r == "-" and value is None or value == "":
return replacement
if r == "+" and value is not None:
return replacement
return value
def split_replacement(self, field_name: str) -> tuple[str, str, str]:
if "-" in field_name:
return field_name.rpartition("-")
if "+" in field_name:
return field_name.rpartition("+")
return field_name, "", ""
def is_strict(self) -> bool:
return self.platform in [Platform.UNIVERSAL, Platform.WINDOWS]
def _vformat(
self,
format_string: str,
@ -72,6 +135,7 @@ class MetadataFormatter(string.Formatter):
if lstrip:
literal_text = literal_text.lstrip("-_)}]#")
if self.smart_cleanup:
literal_text = self.handle_replacements(literal_text, self.replacements.literal_text)
lspace = literal_text[0].isspace() if literal_text else False
rspace = literal_text[-1].isspace() if literal_text else False
literal_text = " ".join(literal_text.split())
@ -87,6 +151,7 @@ class MetadataFormatter(string.Formatter):
lstrip = False
# if there's a field, output it
if field_name is not None and field_name != "":
field_name, r, replacement = self.split_replacement(field_name)
field_name = field_name.casefold()
# this is some markup, find the object and do the formatting
@ -99,6 +164,8 @@ class MetadataFormatter(string.Formatter):
obj, arg_used = self.get_field(field_name, args, kwargs)
used_args.add(arg_used)
obj = self.none_replacement(obj, replacement, r)
# do any conversion on the resulting object
obj = self.convert_field(obj, conversion) # type: ignore
@ -117,6 +184,8 @@ class MetadataFormatter(string.Formatter):
result[-1], _, _ = result[-1].rstrip().rpartition(" ")
result[-1] = result[-1].rstrip("-_({[#")
if self.smart_cleanup:
# colons and slashes get special treatment
fmt_obj = self.handle_replacements(fmt_obj, self.replacements.format_value)
fmt_obj = " ".join(fmt_obj.split())
fmt_obj = str(sanitize_filename(fmt_obj, platform=self.platform))
result.append(fmt_obj)
@ -179,13 +248,6 @@ class FileRenamer:
new_basename = ""
for component in pathlib.PureWindowsPath(template).parts:
if (
self.platform.casefold() in ["universal", "windows"] or sys.platform.casefold() in ["windows"]
) and self.smart_cleanup:
# colons get special treatment
component = component.replace(": ", " - ")
component = component.replace(":", "-")
new_basename = str(
sanitize_filename(fmt.vformat(component, args=[], kwargs=Default(md_dict)), platform=self.platform)
).strip()

View File

@ -524,7 +524,7 @@ class IssueIdentifier:
self.log_msg("")
if len(self.match_list) == 0:
self.log_msg(":-(no matches!")
self.log_msg(":-( no matches!")
self.search_result = self.result_no_matches
return self.match_list

View File

@ -159,7 +159,7 @@ def ctmain() -> None:
logger.debug("Installed Packages")
for pkg in sorted(importlib_metadata.distributions(), key=lambda x: x.name):
logger.debug("%s\t%s", pkg.name, pkg.version)
logger.debug("%s\t%s", pkg.metadata["Name"], pkg.metadata["Version"])
utils.load_publishers()
update_publishers()

View File

@ -340,9 +340,9 @@ class PageListEditor(QtWidgets.QWidget):
else:
text += " (Error: " + page_dict["Type"] + ")"
if "DoublePage" in page_dict:
text += " " + "\U00002461"
text += " "
if "Bookmark" in page_dict:
text += " " + "\U0001F516"
text += " 🔖"
return text
def get_page_list(self) -> list[ImageMetadata]:

View File

@ -175,29 +175,37 @@ class RenameWindow(QtWidgets.QDialog):
center_window_on_parent(prog_dialog)
QtCore.QCoreApplication.processEvents()
for idx, comic in enumerate(zip(self.comic_archive_list, self.rename_list)):
try:
for idx, comic in enumerate(zip(self.comic_archive_list, self.rename_list)):
QtCore.QCoreApplication.processEvents()
if prog_dialog.wasCanceled():
break
idx += 1
prog_dialog.setValue(idx)
prog_dialog.setLabelText(comic[1])
center_window_on_parent(prog_dialog)
QtCore.QCoreApplication.processEvents()
QtCore.QCoreApplication.processEvents()
if prog_dialog.wasCanceled():
break
idx += 1
prog_dialog.setValue(idx)
prog_dialog.setLabelText(comic[1])
center_window_on_parent(prog_dialog)
QtCore.QCoreApplication.processEvents()
folder = get_rename_dir(comic[0], self.settings.rename_dir if self.settings.rename_move_dir else None)
folder = get_rename_dir(comic[0], self.settings.rename_dir if self.settings.rename_move_dir else None)
full_path = folder / comic[1]
full_path = folder / comic[1]
if full_path == comic[0].path:
logger.info("%s: Filename is already good!", comic[1])
continue
if full_path == comic[0].path:
logger.info("%s: Filename is already good!", comic[1])
continue
if not comic[0].is_writable(check_rar_status=False):
continue
if not comic[0].is_writable(check_rar_status=False):
continue
comic[0].rename(utils.unique_file(full_path))
comic[0].rename(utils.unique_file(full_path))
except Exception as e:
logger.exception("Failed to rename comic archive: %s", comic[0].path)
QtWidgets.QMessageBox.critical(
self,
"There was an issue when renaming!",
f"Renaming failed!<br/><br/>{type(e).__name__}: {e}<br/><br/>",
)
prog_dialog.hide()
QtCore.QCoreApplication.processEvents()

View File

@ -1712,7 +1712,7 @@ Have fun!
)
if dlg.ignore_leading_digits_in_filename and md.series is not None:
# remove all leading numbers
md.series = re.sub(r"([\d.]*)(.*)", "\\2", md.series)
md.series = re.sub(r"([\d.]*)(.*)", r"\2", md.series)
# use the dialog specified search string
if dlg.search_string:

View File

@ -1,10 +1,11 @@
beautifulsoup4 >= 4.1
importlib_metadata
beautifulsoup4>=4.1
importlib_metadata>=3.3.0
natsort>=8.1.0
pathvalidate
pillow>=9.1.0
py7zr
pycountry
pyicu; sys_platform == 'linux' or sys_platform == 'darwin'
requests==2.*
text2digits
thefuzz>=0.19.0

Binary file not shown.

View File

@ -750,6 +750,76 @@ fnames = [
]
rnames = [
(
"{series!c} {price} {year}", # Capitalize
False,
"universal",
"Cory doctorow's futuristic tales of the here and now 2007.cbz",
does_not_raise(),
),
(
"{series!t} {price} {year}", # Title Case
False,
"universal",
"Cory Doctorow'S Futuristic Tales Of The Here And Now 2007.cbz",
does_not_raise(),
),
(
"{series!S} {price} {year}", # Swap Case
False,
"universal",
"cORY dOCTOROW'S fUTURISTIC tALES OF THE hERE AND nOW 2007.cbz",
does_not_raise(),
),
(
"{title!l} {price} {year}", # Lowercase
False,
"universal",
"anda's game 2007.cbz",
does_not_raise(),
),
(
"{title!u} {price} {year}", # Upper Case
False,
"universal",
"ANDA'S GAME 2007.cbz",
does_not_raise(),
),
(
"{title} {price} {year+}", # Empty alternate value
False,
"universal",
"Anda's Game.cbz",
does_not_raise(),
),
(
"{title} {price} {year+year!u}", # Alternate value Upper Case
False,
"universal",
"Anda's Game YEAR.cbz",
does_not_raise(),
),
(
"{title} {price} {year+year}", # Alternate Value
False,
"universal",
"Anda's Game year.cbz",
does_not_raise(),
),
(
"{title} {price-0} {year}", # Default value
False,
"universal",
"Anda's Game 0 2007.cbz",
does_not_raise(),
),
(
"{title} {price+0} {year}", # Alternate Value
False,
"universal",
"Anda's Game 2007.cbz",
does_not_raise(),
),
(
"{series} #{issue} - {title} ({year}) ({price})", # price should be none
False,
@ -757,6 +827,13 @@ rnames = [
"Cory Doctorow's Futuristic Tales of the Here and Now #001 - Anda's Game (2007).cbz",
does_not_raise(),
),
(
"{series} #{issue} - {title} {volume:02} ({year})", # Ensure format specifier works
False,
"universal",
"Cory Doctorow's Futuristic Tales of the Here and Now #001 - Anda's Game 01 (2007).cbz",
does_not_raise(),
),
(
"{series} #{issue} - {title} ({year})({price})", # price should be none, test no space between ')('
False,
@ -778,6 +855,27 @@ rnames = [
"Cory Doctorow's Futuristic Tales of the Here and Now #001 - Anda's Game (2007).cbz",
does_not_raise(),
),
(
"{title} {web_link}", # Ensure colon is replaced in metadata
False,
"universal",
"Anda's Game https---comicvine.gamespot.com-cory-doctorows-futuristic-tales-of-the-here-and-no-4000-140529-.cbz",
does_not_raise(),
),
(
"{title} {web_link}", # Ensure slashes are replaced in metadata on linux/macos
False,
"Linux",
"Anda's Game https:--comicvine.gamespot.com-cory-doctorows-futuristic-tales-of-the-here-and-no-4000-140529-.cbz",
does_not_raise(),
),
(
"{series}:{title} #{issue} ({year})", # on windows the ':' is replaced
False,
"universal",
"Cory Doctorow's Futuristic Tales of the Here and Now-Anda's Game #001 (2007).cbz",
does_not_raise(),
),
(
"{series}: {title} #{issue} ({year})", # on windows the ':' is replaced
False,

View File

@ -1,5 +1,6 @@
from __future__ import annotations
import platform
import shutil
import pytest
@ -15,6 +16,8 @@ def test_getPageNameList():
pageNameList = c.get_page_name_list()
assert pageNameList == [
"!cover.jpg",
"00.jpg",
"page0.jpg",
"Page1.jpeg",
"Page2.png",
@ -44,6 +47,58 @@ def test_save_cix(tmp_comic):
md = tmp_comic.read_cix()
def test_save_cbi(tmp_comic):
md = tmp_comic.read_cix()
md.set_default_page_list(tmp_comic.get_number_of_pages())
assert tmp_comic.write_cbi(md)
md = tmp_comic.read_cbi()
@pytest.mark.xfail(not (comicapi.comicarchive.rar_support and shutil.which("rar")), reason="rar support")
def test_save_cix_rar(tmp_path):
cbr_path = datadir / "fake_cbr.cbr"
shutil.copy(cbr_path, tmp_path)
tmp_comic = comicapi.comicarchive.ComicArchive(tmp_path / cbr_path.name)
assert tmp_comic.write_cix(comicapi.genericmetadata.md_test)
md = tmp_comic.read_cix()
assert md.replace(pages=[]) == comicapi.genericmetadata.md_test.replace(pages=[])
@pytest.mark.xfail(not (comicapi.comicarchive.rar_support and shutil.which("rar")), reason="rar support")
def test_save_cbi_rar(tmp_path):
cbr_path = datadir / "fake_cbr.cbr"
shutil.copy(cbr_path, tmp_path)
tmp_comic = comicapi.comicarchive.ComicArchive(tmp_path / cbr_path.name)
assert tmp_comic.write_cbi(comicapi.genericmetadata.md_test)
md = tmp_comic.read_cbi()
assert md.replace(pages=[]) == comicapi.genericmetadata.md_test.replace(
pages=[],
day=None,
alternate_series=None,
alternate_number=None,
alternate_count=None,
imprint=None,
notes=None,
web_link=None,
format=None,
manga=None,
page_count=None,
maturity_rating=None,
story_arc=None,
series_group=None,
scan_info=None,
characters=None,
teams=None,
locations=None,
)
def test_page_type_save(tmp_comic):
md = tmp_comic.read_cix()
t = md.pages[0]
@ -96,3 +151,17 @@ def test_rename(tmp_comic, tmp_path):
assert not old_path.exists()
assert tmp_comic.path.exists()
assert tmp_comic.path != old_path
def test_rename_ro_dest(tmp_comic, tmp_path):
old_path = tmp_comic.path
dest = tmp_path / "tmp"
dest.mkdir(mode=0o000)
with pytest.raises(OSError):
if platform.system() == "Windows":
raise OSError("Windows sucks")
tmp_comic.rename(dest / "test.cbz")
dest.chmod(mode=0o777)
assert old_path.exists()
assert tmp_comic.path.exists()
assert tmp_comic.path == old_path

View File

@ -18,7 +18,6 @@ def test_cbi():
md = CBI.metadata_from_string(string)
md_test = comicapi.genericmetadata.md_test.replace(
day=None,
language="English",
page_count=None,
maturity_rating=None,
story_arc=None,

View File

@ -63,25 +63,26 @@ def test_xlate(value, result):
language_values = [
("en", "English"),
("EN", "English"),
("En", "English"),
("", None),
("english", "en"),
("ENGLISH", "en"),
("EnglisH", "en"),
("", ""),
("aaa", None), # does not have a 2-letter code
(None, None),
]
@pytest.mark.parametrize("value, result", language_values)
def test_get_language(value, result):
assert result == comicapi.utils.get_language(value)
def test_get_language_iso(value, result):
assert result == comicapi.utils.get_language_iso(value)
def test_unique_file(tmp_path):
file = tmp_path / "test"
file = tmp_path / "test.cbz"
assert file == comicapi.utils.unique_file(file)
file.mkdir()
assert (tmp_path / "test (1)") == comicapi.utils.unique_file(file)
assert (tmp_path / "test (1).cbz") == comicapi.utils.unique_file(file)
def test_add_to_path(monkeypatch):